尽管依赖项注入 (也称为“ DI”)是一种在OOP中组成对象的自然技术(在Martin Fowler引入该术语之前就已知道),但Spring IoC , Google Guice , Java EE6 CDI , Dagger和其他DI框架将其转变为反模式。

我将不讨论反对“ setter注入”(例如在Spring IoC中 )和“现场注入”(例如在PicoContainer中 )的明显论点。 这些机制仅违反了面向对象编程的基本原理,并鼓励我们创建不完整的可变对象,这些对象在应用程序执行过程中会塞满数据。 请记住:理想对象必须是不可变的 , 并且不能包含setter 。

相反,让我们讨论一下“构造函数注入”(例如Google Guice中的方法 )及其与依赖注入容器的结合使用 。 我将尝试说明为什么至少将这些容器视为冗余。

什么是依赖注入?

这就是依赖项注入(与普通的旧对象组成没有真正的区别):

public class Budget {private final DB db;public Budget(DB data) {this.db = data;}public long total() {return this.db.cell("SELECT SUM(cost) FROM ledger");}
}

对象data称为“依赖关系”。

Budget不知道它正在使用哪种数据库。 它需要从数据库中获得的所有功能,就是能够通过cell()方法使用任意SQL查询来获取单元cell() 。 我们可以使用DB接口的PostgreSQL实现实例化Budget ,例如:

public class App {public static void main(String... args) {Budget budget = new Budget(new Postgres("jdbc:postgresql:5740/main"));System.out.println("Total is: " + budget.total());}
}

换句话说,我们正在将依赖项“注入”到新的对象budget

这种“依赖注入”方法的替代方法是让Budget决定要使用的数据库:

public class Budget {private final DB db = new Postgres("jdbc:postgresql:5740/main");// class methods
}

这非常脏,导致1)代码重复,2)无法重用和3)无法测试等。无需讨论原因。 很明显。

因此,通过构造函数进行依赖注入是一种了不起的技术。 好吧,真的没有技术。 更像Java和所有其他面向对象语言的功能。 预计几乎所有对象都希望封装一些知识(也称为“状态”)。 这就是构造函数的用途。

什么是DI容器?

到目前为止,一切都很好,但是这里面是阴暗的一面-依赖项注入容器。 它是这样工作的(让我们以Google Guice为例):

import javax.inject.Inject;
public class Budget {private final DB db;@Injectpublic Budget(DB data) {this.db = data;}// same methods as above
}

注意:构造函数带有@Inject注释。

然后,我们应该在应用程序启动时在某处配置一个容器:

Injector injector = Guice.createInjector(new AbstractModule() {@Overridepublic void configure() {this.bind(DB.class).toInstance(new Postgres("jdbc:postgresql:5740/main"));}}
);

一些框架甚至允许我们在XML文件中配置注入器。

从现在开始,我们将无法像以前一样通过new运算符实例化Budget 。 相反,我们应该使用刚创建的注射器:

public class App {public static void main(String... args) {Injection injector = // as we just did in the previous snippetBudget budget = injector.getInstance(Budget.class);System.out.println("Total is: " + budget.total());}
}

注入自动发现,要实例化Budget它必须为其构造函数提供一个参数。 它将使用我们在注射器中实例化的Postgres类的实例。

这是使用Guice的正确和推荐的方法。 不过,有一些甚至更暗的模式,这是可能的,但不建议这样做。 例如,您可以使喷射器成为单例,并在Budget类中直接使用它。 这些机制甚至被DI容器制造商都认为是错误的,因此,让我们忽略它们,重点关注推荐的方案。

这个是来做什么的?

让我重申并总结不正确使用依赖项注入容器的场景:

  • 现场注入
  • 二传手注射
  • 将注入器作为依赖项
  • 使注入器成为全局单例

如果我们把它们都放在一边,剩下的就是上面解释的构造函数注入。 那对我们有什么帮助? 我们为什么需要它? 为什么我们不能在应用程序的主类中使用普通的new

如果使用XML,我们创建的容器只是向代码库添加了更多行,甚至添加了更多文件。 它除了增加了复杂性之外没有增加任何东西。 如果遇到以下问题,我们应该永远记住这一点:“哪个数据库用作预算的论点?”

正确的方式

现在,让我向您展示一个使用new构造应用程序的真实示例。 这是我们在rultor.com中创建“思维引擎”的方式 (完整的类在Agents.java ):

final Agent agent = new Agent.Iterative(new Array(new Understands(this.github,new QnSince(49092213,new QnReferredTo(this.github.users().self().login(),new QnParametrized(new Question.FirstOf(new Array(new QnIfContains("config", new QnConfig(profile)),new QnIfContains("status", new QnStatus(talk)),new QnIfContains("version", new QnVersion()),new QnIfContains("hello", new QnHello()),new QnIfCollaborator(new QnAlone(talk, locks,new Question.FirstOf(new Array(new QnIfContains("merge",new QnAskedBy(profile,Agents.commanders("merge"),new QnMerge())),new QnIfContains("deploy",new QnAskedBy(profile,Agents.commanders("deploy"),new QnDeploy())),new QnIfContains("release",new QnAskedBy(profile,Agents.commanders("release"),new QnRelease())))))))))))),new StartsRequest(profile),new RegistersShell("b1.rultor.com", 22,"rultor",IOUtils.toString(this.getClass().getResourceAsStream("rultor.key"),CharEncoding.UTF_8)),new StartsDaemon(profile),new KillsDaemon(TimeUnit.HOURS.toMinutes(2L)),new EndsDaemon(),new EndsRequest(),new Tweets(this.github,new OAuthTwitter(Manifests.read("Rultor-TwitterKey"),Manifests.read("Rultor-TwitterSecret"),Manifests.read("Rultor-TwitterToken"),Manifests.read("Rultor-TwitterTokenSecret"))),new CommentsTag(this.github),new Reports(this.github),new RemovesShell(),new ArchivesDaemon(new ReRegion(new Region.Simple(Manifests.read("Rultor-S3Key"),Manifests.read("Rultor-S3Secret"))).bucket(Manifests.read("Rultor-S3Bucket"))),new Publishes(profile))
);

印象深刻? 这是真正的对象组成。 我相信这是应该实例化正确的面向对象应用程序的方式。

和DI容器? 我认为,它们只会增加不必要的噪音。

相关文章

您可能还会发现以下有趣的帖子:

  • 吸气剂/设定者。 邪恶。 期。
  • OOP中的反模式
  • 避免字符串串联
  • 对象应该是不可变的
  • 为什么NULL是错误的?

翻译自: https://www.javacodegeeks.com/2014/10/di-containers-are-code-polluters.html

DI容器是代码污染者相关推荐

  1. di容器_DI容器是代码污染者

    di容器 尽管依赖项注入 (aka," DI")是一种在OOP中组成对象的自然技术(在Martin Fowler引入该术语之前就已知道),但Spring IoC , Google ...

  2. 百行代码打造一个DI容器(支持瞬时生命周期、单利生命周期、构造函数自动注入、属性自动注入、字段自动注入)...

    DI注入在.Net平台是非常流行的, 很多项目都用到了,很多开发人员或多或少也用到DI容器了,感觉DI容器很神奇很厉害.本文将通过百行代码展示DI容器的内部核心代码(包括组件的瞬时生命周期.单利生命周 ...

  3. 超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享...

    超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享 DI框架 Google-Guice入门介绍 转载于:https://www.cnblogs.com ...

  4. 通过编写DI容器来了解依赖注入-从头开始! (第2部分)

    这是我的" DI从零开始"系列的第二部分. 在上一篇文章中,我们讨论了我们的基本示例以及"手动"方法存在的问题. 现在,我们要使服务网络的布线自动化. DI S ...

  5. java Android创建容器,Java-在具有(没有)DI容器的Android中正确进行依赖项注入(匕首1)...

    我目前正在开发(实际上是在构建)具有蓝牙连接和功能的Android应用: RESTful服务的HTTP通信.我碰巧遇到了一个名为Dagger的"依赖注入"框架,这对我来说是革命性的 ...

  6. Effective_STL 学习笔记(二)小心对 “容器无关代码” 的幻想

    STL 是建立在泛化上的,数组泛化为容器,参数化了所包含的对象的类型.函数泛化为算法,参数化了所用的迭代器类型.指针泛化为迭代器,参数化了所指向对象的类型. 泛化继续,独立的容器类型泛化为序列或关联容 ...

  7. 【linux】容器之代码自动发布-docker

    一.分析 旧: 代码发布环境提前准备,以主机为颗粒度 静态 新: 代码发布环境 多套,以容器为颗粒度 编译 二.业务发布逻辑设计图 三.工具使用流程图 工具 git gitlab jenkins to ...

  8. 尺度不变性是指什么不变_不变性如何提供帮助

    尺度不变性是指什么不变 在最近的几篇文章中,包括" Getters / Setters. 邪恶. 期." , "对象应该是不可变的"和"依赖注入容器是 ...

  9. app访问java web_Java Web App体系结构

    app访问java web 我曾经利用Servlet,JSP,JAX-RS,Spring框架,Play框架,带有Facelets的JSF和一些Spark框架. 以我的拙见,所有这些解决方案都远非面向对 ...

最新文章

  1. LPC单片机IO口默认状态、复位状态、未初始化时输出高电平处理
  2. linux push path,Linux系统shell使用几点摘录(二)
  3. [html] 请说说你在写布局时对于浏览器兼容性的感受或总结
  4. Jaeger插件开发及背后的思考
  5. 【Proteus仿真】51单片机驱动蜂鸣器播放《天空之城》
  6. Excel表格中如何批量删除工作表
  7. 将VSCode添加到鼠标右键菜单
  8. hadoop 运行原理
  9. 百度地图API秘钥问题
  10. 单片机C51继电器控制C语言,51单片机对继电器的控制
  11. 论文代码复现 | 无人机与卡车联合配送(Python+Gurobi)(The flying sidekick traveling salesman problem)
  12. java操作zip压缩文件加密码和解密工具类
  13. 【英语:发音基础】A1.元音与辅音
  14. 解读 2018:13 家开源框架谁能统一流计算?
  15. 软件架构非功能需求——可靠性
  16. 兆骑科创高层次人才引进双创平台,双创服务,赛事路演
  17. 作为一个入门编程小白的感触
  18. mysql创建新闻发布时间_基于PHP+mysql实现新闻发布系统的开发
  19. 网站文章采集、撰写、推广注意要点
  20. 洗料系列-杂谈篇-麻将自动化---第一章、麻将基础入门

热门文章

  1. 一个java源文件中可以声明多少个class与编译后会生成多少个字节码文件
  2. 希尔排序+移位法(吊打交换法)
  3. Android中SlidingDrawer开发报错You need to use a Theme.AppCompat theme (or descendant) with this activity.
  4. 转: Spark 的核心概念 RDD
  5. dmn是大脑中哪个区域_DMN中的函数式编程:感觉就像再次重读我的大学课程一样...
  6. apache.camel_Apache Camel 3.1 –更多骆驼核心优化(第3部分)
  7. win7 activemq_带有骆驼,ActiveMQ,Elasticsearch的关键HL7用例
  8. 使用Selenium自动化测试处理多个浏览器选项卡
  9. JDK 13:VM.events已添加到jcmd
  10. gc 吞吐量和停顿时间_GC对吞吐量和延迟的影响