DI容器是代码污染者

尽管依赖项注入 (也称为“ 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

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

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

相关文章

java程序-类的高级特性

创建Employee类,在类中定义三个属性:编号,姓名,年龄,然后在构造方法里初始化这三个属性,最后在实现接口中的定义的CompareTo方法,将对象按编号升序排列。 代码如下:(程序可能有些错误…

js 数组去重

数组去重的思路&#xff0c;突然感觉挺有趣的&#xff0c;来整理一下 两个 for 循环比较的&#xff0c;如下function removeRepeat(arr){ for( var i 0; i < arr.length; i ){ for ( var j i 1; j < arr.length; i ){ if ( a…

CSS中实现水平/垂直居中

CSS中实现水平/垂直居中 在CSS中实现水平居中相对简单&#xff0c;但是却没有一个明确的属性表示这是实现垂直居中的&#xff0c;这就导致垂直居中的实现相对初学者来说难上许多。但是在实际的开发中垂直居中的需求常常出现&#xff0c;例如一行中有左右两部分&#xff0c;左边…

SWF 文件不能访问本地资源 只有仅限于文件系统的 SWF 文件和可信的本地 SWF 文件可以访问本地资源。...

错误信息&#xff1a;SecurityError: Error #2148: SWF 文件 D:/demo/test/index.swf 不能访问本地资源 D:/demo/test/bin-debug/textLayout_4.0.0.10485.swf。只有仅限于文件系统的 SWF 文件和可信的本地 SWF 文件可以访问本地资源。 at flash.net::URLStream/load() at fla…

高并发系统之大忌-慢查询

最近又遇到了一次慢查把db&#xff08;mariadb10)几乎打挂的案例&#xff0c;作为一个核心支付系统的技术负责人&#xff0c;真是每日如履薄冰。因为之前支付系统经常出问题&#xff0c;现在各个BG对支付系统都盯得很紧。这次要不是我及时让DB给暴力清理数据&#xff0c;没准又…

CSS媒体查询

格式&#xff08;style&#xff1a;{}&#xff09;元素设置&#xff1a; body{background-color:#0033FF;}/*媒体查询:当页面宽度最大为960px时*/media screen and (max-width: 960px){/*body背景颜色为*/body{background-color:#FF6699}}media screen and (max-width: 768px){…

(单元测试)JavaBeans的技巧

如果您正在编写Java代码&#xff0c;那么您至少要编写一些遵循JavaBean约定的类&#xff0c;即&#xff0c;具有带有公共getter和setter方法的私有属性的类&#xff0c;包含无参数的构造函数&#xff0c;可序列化&#xff0c;并且遵守Equals和HashCode合同。 最重要的是&#x…

js中的正则表达式

正则表达式等名称并不能让人一下就能明白是什么意思&#xff0c;我认为称之为 规则表达式 更为合理&#xff0c;就是描述一个字符串规则的表达式。 语法 正则表达式的语法有两种&#xff0c;下面第一种语法稍显 怪异&#xff0c;但确实是合法的并且相对方便&#xff0c;推荐。…

Hadoop namenode启动瓶颈分析

转载&#xff1a;http://blog.csdn.net/AE86_FC/archive/2010/08/26/5842020.aspx NameNode启动过程详细剖析 NameNode中几个关键的数据结构 FSImage Namenode会将HDFS的文件和目录元数据存储在一个叫fsimage的二进制文件中&#xff0c;每次保存fsimage之后到下次保存之间的所有…

Java 9 –终极功能列表

这篇文章将针对即将到来的Java 9版本进行更新&#xff0c;新增功能 &#xff08; 最新更新&#xff1a;2014年 9月9日 &#xff09; OpenJDK开发正在加快速度&#xff1a;2014年3月Java 8发布之后&#xff0c;我们预计将进入2年的发布周期。 据报道&#xff0c;Java 9将于2016…

2018.3.30 边框应用与导航栏设置

<!DOCTYPE html><html> <head> <meta charset"utf-8" /> <title></title> <style type"text/css"> /*制作三角形*/ #one{ width: 0px; …

centos 更改用户登录宿主机时间

chage -l username (查看用户登录时间&#xff1a;username为查看的用户) chage -M 90 username &#xff08;将用户登录主机的时间更改为90天&#xff09; 转载于:https://www.cnblogs.com/MUQINGFENG123/p/11075912.html

js中的作用域和作用域链

作用域就是变量与函数的可访问范围。在js中只有 全局作用域 和 函数作用域 &#xff0c;并没有块级作用域。 全局作用域 在所有函数外定义的变量、声明的函数就是全局作用域&#xff0c;在全部环境下都可以访问。 var a 111;function fn(){console.log(a); }fn(); // 打印了…

CryEngine3 引擎非商业用途将免费

CryEngine3引擎非商业用途将免费:8月发布 http://t.cn/hdEr45】Crytek日前宣布将在今年8月免费推出CryEngine3引擎的开发包&#xff0c;登录Crytek官网注册&#xff0c;签署保密协议保证不将CryEngine3用作商业用途&#xff0c;就可获得CryEngine3软件授权。Crytek官网上的一份…

vue打包后不使用服务器直接访问方法

根据官网打包执行npm run build 后dist文件夹打开的index.html 是空白 需要开启http服务器才能访问&#xff0c;以下是解决办法 1、找到config文件夹下的index文件 修改成 2、找到build文件夹下的until文件 修改成 然后执行npm run build重新打包下就ok了 更多专业前端知…

IntelliJ中的键盘快捷键

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

OpenStack虚机网卡的创建过程

原文&#xff1a;https://www.sdnlab.com/20286.htmlOpenStack最基本和常用的操作就是启动虚机。虚机启动的过程中涉及很多内容&#xff0c;其中非常重要的一个环节就是创建并绑定虚机的虚拟网卡。虚机的创建和管理是Nova的任务&#xff0c;虚机网络的创建和管理是Neutron的任务…

js中的原型与原型链

js的学习有三座大山&#xff0c; 原型/原型链 、 作用域/闭包 、 异步/单线程&#xff0c;这三个知识点虽然基础但是入门时理解起来比较困难&#xff0c;本文先整理总结原型和原型链这一知识点。 1. 原型链怎么来的&#xff1f;对象的原型和function的prototype属性有什么关系…

线性表--算法设计题2.29

已知A&#xff0c;B和C为三个递增有序的线性表&#xff0c;现要求对A表作如下操作&#xff1a;删去那些既在B表中出现又在C表中出现的元素。试对顺序表编写实现上述操作的算法.&#xff08;注意&#xff1a;题中没有特别指明同一表中的元素值各不相同&#xff09;。C code: #in…

HTML5 audio 如何实现播放多个MP3音频

<audio>标签是HTML5中的新标签&#xff0c;定义声音用于嵌入音频内容&#xff0c;比如音乐或其他音频流。用的比较多音频格式是.mp3。 <audio>标签常用属性如下表 属性值描述autoplayautoplay添加该属性后&#xff0c;音频会自动播放controlscontrols设置后&…