DI容器
了解设计的第一步:模型
理解一个模型的关键在于,要了解这个模型设计的来龙去脉,知道它是如何解决相应的问题。
耦合的依赖
DI(Dependency Injection)容器,解决的是组件创建和组装的问题。
例子:假设有一个AritcleService提供根据标题查询文章的功能。当然,数据需要持久化的,所以,这里还有一个ArticleRepository,用来与持久化数据打交道。
- repository层:和DB进行连接
- service层:调用repository
- 调用层:操作全体的层
repository层
由于ArticleRepository需要和DB进行连接,一个直接的做法就是在ArticleRepository中增加一个字段来表示DB
1 | package repository |
service层
在ArticleService处理业务的过程中,需要用到ArticleRepository辅助它完成功能,也就是说,ArticleService要依赖于ArticleRepository。
一个直接的做法是在ArticleService中增加一个字段表示ArticleRepository。
1 | package service |
调用层
初始化service,固定的repository和db也会被生成。
1 | package main |
问题所在
一旦开始准备测试,就会发现,如果想要ArticleService跑起来,那就的让ArticleReposiotry也跑起来;要让ArticleRepository跑起来,那就得准备database连接,还得准备database里面的各种数据。
而在真实的项目中,构建一个对象可能还会牵扯到更多内容:
- 根据不同的参数,创建不同的实现类对象,可能需要用到工厂模式。
- 为了了解方法的执行时间,需要给被依赖的对象加上监控。
- 依赖的对象来自于某个框架,不知道具体的实现类是什么
分离的依赖
最简单的办法就是把创建对象的过程拿出去。只留下与字段关联的过程
- repository层变成这样
db的连接和实例创建在外部进行,NewArticleRepository中传入的是db实例。
1 | package repository |
- service层变成这样
1 | package service |
Constructor Injection
构造函数注入。引入间接层(接口),即New方法返回接口类型,来解耦各个层之间的依赖关系。
- repository层
把已经实例化的db作为参数传入NewArticleRepository。而且NewArticleRepository的return返回的是接口类型,service不直接依赖repository,而是去依赖repository定义好的接口,解耦了repository层和service层的依赖关系。
1 | package repository |
- service层
把已经实例化的ArticleRepository接口类型作为参数传入NewArticleService中,因此不再依赖repository。return类型为ArticleService的接口类型,因此也解耦了service层和最终的操作层的依赖关系。
1 | package service |
- 调用层
在这里进行所有的对象创建和组装。
ar := repository.NewArticleRepository(db)
:可以接受任何db类型(mysql,redis,PostgreSQL等)
1 | package main |
然而,由于这里进行了所有的对象创建和组装,会导致代码很臃肿。因此,引入DI container,让所有对象的创建和组装都在一个container中完成,再从container中取出已经实例化的对象。
参考:sarulabs/di