
依赖注入相信大家都不陌生也不是什么新鲜的概念了,笔者初次深切体会依赖注入这种设计模式是在16年笔者在学习phalapi框架时使用的DI()函数,简单来说就是将所有的资源初始化集中在一起,通过统一的容器对外提供,而不是通过全局变量或到处New的方式。最近在学习kratos又看到一个团队的历史项目都使用wier,一个是正面教材一个是反面教材也有一些自己的思考,希望能够分享出来和大家交流交流。
资料如下:
其实笔者开始写Goalng包括团队内部自己研发的小框架go-core主要使用的是资源容器的方式,框架帮助你将资源统一的容器管理起来(也是一种间接的全局变量的方式,只是不能直接进行修改),但对于业务分层如果是微服务的话足够简单就直接 packagename.funcname 进行调用,如果是稍微复杂一些的项目用一个空的status的全局变量来提供func的隔离。
但这样的设计会存在一下几个问题:
如果你使用成熟框架无论是go-zero或kratos都应该按照他们的最佳实践来执行,如果是框架层没有约束还是要更具自己的实际情况来进行选择,有限选择最简单的方式顺应研发人员的直觉,设计模式如果用不对那将是场灾难,就和最近接触到了一个项目虽然使用了wier作为依赖注入,但是用的一塌糊涂:
它存在的问题:
一个设计模式如果没有用对,虽然解决了上面资源共享的问题,但是又带来了一堆奇怪的问题,真的是得不偿失。所以架构也好业务也好都是逐步演进过来的,不要将设计模式当做万能药,第一性原则还是保持简单和高内聚低耦合,遇到文件引入对应的方案然后需要做好落地的最佳实践。
当笔者接触到上面的项目之后就非常的迷茫,在思考怎么样才是一个好的依赖注入设计模式的落地呢?当接触到 kratos 之后给了我一些启发:
一个好的依赖注入需要具备哪些特性
bilibili/kratos × google/wire 标准样例:
// 定义依赖关系,从头到尾
func wireApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) {
panic(wire.Build(
server.ProviderSet,
data.ProviderSet,
biz.ProviderSet,
service.ProviderSet,
newApp))
}
// 初始化方法也要参与依赖注入
func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server) *kratos.App {
return kratos.New(
kratos.ID(id),
kratos.Name(Name),
kratos.Version(Version),
kratos.Metadata(map[string]string{}),
kratos.Logger(logger),
kratos.Server(
gs,
hs,
),
)
}
// 程序启动直接从wier生成的方法中获取
app, cleanup, err := wireApp(bc.Server, bc.Data, logger)
if err != nil {
panic(err)
}
defer cleanup()
// start and wait for stop signal
if err := app.Run(); err != nil {
panic(err)
}
依赖的资源都是局部的,并且只关注自己所依赖的范围,依赖的依赖怎么注入不应该被关注
// GreeterService is a greeter service.
type GreeterService struct {
v1.UnimplementedGreeterServer
uc *biz.GreeterUsecase
}
// NewGreeterService new a greeter service.
func NewGreeterService(uc *biz.GreeterUsecase) *GreeterService {
return &GreeterService{uc: uc}
}
...
// GreeterUsecase is a Greeter usecase.
type GreeterUsecase struct {
repo GreeterRepo
log *log.Helper
}
// NewGreeterUsecase new a Greeter usecase.
func NewGreeterUsecase(repo GreeterRepo, logger log.Logger) *GreeterUsecase {
return &GreeterUsecase{repo: repo, log: log.NewHelper(logger)}
}