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

di容器

尽管依赖项注入 (aka,“ 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容器? 我认为,它们只会增加不必要的噪音。

相关文章

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

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

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

di容器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/339360.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C/C++高级算法之绘制曼德布洛特集

今晚课题【长按识别图中二维码】

python mysql 连接6_寒假学习进度-6(Python连接MySQL数据库)

Python连接mysql和操作软件:pycharm开始在pycharm下面的Terminal中安装mysql时提醒pip版本不够,所以需要先升级一下pippython -m pip install --upgrade pip升级完pip之后就可以下载mysqlpip install mysql下载完成后在setting中查看进行代码测试#!/usr/…

intellij 快捷键_IntelliJ中的键盘快捷键

intellij 快捷键我上周参加了Hadi Hariri在JavaOne上的演讲。 他介绍了很多我不知道的IntelliJ键盘快捷键。 非常有用的谈话。 我在下面列出了一些最有用的。 Cmd-1:将焦点移到“项目”窗口 在此输入任何类名(包括使用Camel Case,例如HW来查…

C/C++中*和的用法详解

C中&和*的用法一直是非常让人头疼的难点,课本博客上讲这些知识点一般都是分开讲其用法的,没有详细的总结,导致我在这方面的知识结构格外混乱,在网上找到了一篇英文文章简单总结了这两个符号的一些用法,都是一些比较…

java 实现按规则自增功能_java运算符详解 - osc_74vaali6的个人空间 - OSCHINA - 中文开源技术交流社区...

java基础(2)--运算符详解自增自减规则自增自减就是常用的 a a 或者 a a;前者是后缀式,先把a赋值给a,再加一;后者是后缀式,先加一,在赋值给a;而且a,a并不是原子操作,在当线程环境下并不会出错,当放在多线程…

jsf 单元测试_构建和测试JSF.next

jsf 单元测试JSF 2.3专家组正在努力确定哪些功能将成为即将发布的版本的一部分。 JSF团队一直在努力改善CDI的一致性。 JSF 2.3代码库中已经有许多新功能可以开始测试。 我将尝试使本文保持最新状态,并添加最新功能。 有关最新参考,请参阅Manfred的博客。…

C++ 异常的详细介绍

C 异常的详解程序有时会遇到运行阶段错误,导致程序无法正常执行下去。c异常为处理这种情况提供了一种功能强大的而灵活的工具。异常是相对比较新的C功能,有些老编译器可能没有实现。另外,有些编译器默认关闭这种特性,我们可能需要…

php中的fpm_PHP中的FPM是做什么的

FPM是FastCGI Process Manager的缩写,由全称我们可以知道其和FastCGI有关,就是一个FastCGI的进程管理器。对于FastCGI我们可以理解成一个协议,儿FPM是其在PHP中的一个实现。FPM的出现实现了PHP应用的分布式部署,这样使得PHP和web服…

neo4j cypher_Neo4j:Cypher –避免热切

neo4j cypher当心渴望的管道 尽管我喜欢Cypher的LOAD CSV命令使它容易地将数据获取到Neo4j中的方法,但它目前打破了最不惊奇的规则,因为它急切地在所有行中加载某些查询,即使是那些使用定期提交的查询。 这是我的同事Michael在他的第二篇博…

C++实现简单走迷宫的代码

用n*n个小方格代表迷宫,每个方格上有一个字符0或1,0代表这个格子不能走,1代表这个格子可以走。只能一个格子一个走,而且只能从一个格子向它的上、下、左、右四个方向走,且不能重复。迷宫的入口和出口分别位于左上角和右…

php实现文本替换,php文本替换 函数 strtr()、str_repalce()

php 中替换函数主要有strtr(),str_repalce()这两个函数,但你们都知道他们这两个函数的区别和用法吗?IT我领先在这里给大家一个实例来说明这一问题.先来看看这个php字符串替换函数 strtr()的两种用法:strtr(string,from,to)或者strtr(string,array)首先针对strtr函数第一种方式…

lucene使用3.0.3_Apache Lucene 5.0.0即将发布!

lucene使用3.0.3终于,在一系列强大的4.x功能(最近是4.10.2)发布之后,我们终于在努力开发另一个主要的Apache Lucene版本! 没有确切时间的承诺(完成时就完成了!),但是我们…

利用C/C++实现较完整贪吃蛇游戏

利用空余时间写出了这么一个简单的小游戏,直接使用dos界面运行。那么问题来了,如何让一个字符在dos界面上自由移动???对于这个问题我采用的解决方案是实现gotoxy函数来控制指针位置从而实现字符的移动。那么我们就先来…

php跳转到另外一个方法,PHP 页面跳转到另一个页面的多种方法方法总结

PHP 页面跳转到另一个页面的多种方法方法总结一用 HTTP 头信息也就是用 PHP 的 HEADER 函数 PHP 里的 HEADER 函数的作用就是向浏览器发出由 HTTP 协议规定的本来应该通过 web 服务器的控制指令, 例如声明返回信息的类型 ("Context-type: xxx/xxx"), 页面的属性(&quo…

java 泛型和类型擦除_关于Java泛型和擦除

java 泛型和类型擦除“编译期间擦除泛型”是常识&#xff08;好吧&#xff0c;类型参数和实参实际上是被擦除的&#xff09;。 这是由于“类型擦除”而发生的。 但这是错误的&#xff0c;正如许多开发人员所假设的那样&#xff0c;将<..>符号内指定的所有内容都删除了。 …

PHP中文无法查询,php 中htmlentities导致中文无法查询问题

在php中htmlspecialchars&#xff0c; 将特殊字元转成 HTML 格式,而htmlentities&#xff0c;将所有的字元都转成 HTML 字串 了&#xff0c;下面我来分别简单的介绍。htmlentities用法$str "John & Adams";echo htmlentities($str, ENT_COMPAT);echo "&quo…

C++中的函数汇总(新手必知)!

math.h 数学函数库,一些数学计算的公式的具体实现是放在math.h里,具体有&#xff1a;1 三角函数double sin (double); double cos (double); double tan (double);2 反三角函数double asin (double); 结果介于[-PI/2, PI/2] double acos (double); 结果介于[0, PI] double …

艾伦图灵_Java英雄:丹·艾伦

艾伦图灵“ Java英雄 ”系列休息了很长时间。 老实说&#xff0c;我想即使有很多人我想把它包括在这里&#xff0c;它也可能会在茫茫荒野中终结。 丹是其中之一。 我第一次要求他捐款已经差不多一年半了&#xff0c;并且在此期间发生的所有事情使我安然无not。 但是以下内容在J…

26条C++的经典语录,哪几句戳中你的心!

2019年悄悄的已经过了三分之二&#xff0c;C你了解了多少&#xff1f;随着互联网的发展&#xff0c;c作为底层语言的龙头老大&#xff0c;是时候站出来了&#xff01;如何学好C,老夫我利用平生所学&#xff0c;整理出26条C经典语录&#xff0c;希望能帮助到每一个热爱C的同学&a…

java打印等边三角市,java 打印菱形和等边三角形

public class PrintJava {public static void print(int n){for(int i 1;i<n;i){for(int k n-1;k>i;k–){System.out.print(” “);}for(int j 1;j<2*i-1;j){System.out.print(“*”);}System.out.println(“”);}for(int i n-1 ;i>0;i–){for(int k 1;k<n…