di容器_DI容器是代码污染者
di容器
盡管依賴項(xiàng)注入 (aka,“ DI”)是一種在OOP中組成對(duì)象的自然技術(shù)(在Martin Fowler引入該術(shù)語(yǔ)之前就已知道),但Spring IoC , Google Guice , Java EE6 CDI , Dagger和其他DI框架將其轉(zhuǎn)變?yōu)榉茨J健?
我將不討論反對(duì)“ setter注入”(例如在Spring IoC中 )和“現(xiàn)場(chǎng)注入”(例如在PicoContainer中 )的明顯論點(diǎn)。 這些機(jī)制只是違反了面向?qū)ο缶幊痰幕驹?#xff0c;并鼓勵(lì)我們創(chuàng)建不完整的可變對(duì)象,這些對(duì)象在應(yīng)用程序執(zhí)行過(guò)程中會(huì)塞滿數(shù)據(jù)。 請(qǐng)記住:理想對(duì)象必須是不可變的 , 并且不能包含setter 。
相反,讓我們討論“構(gòu)造函數(shù)注入”(例如Google Guice中的方法 )及其與依賴項(xiàng)注入容器的結(jié)合使用 。 我將嘗試表明為什么至少將這些容器視為冗余。
什么是依賴注入?
這就是依賴項(xiàng)注入(與普通的舊對(duì)象組成沒(méi)有真正的區(qū)別):
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");} }對(duì)象data稱為“依賴關(guān)系”。
Budget不知道它正在使用哪種數(shù)據(jù)庫(kù)。 它需要從數(shù)據(jù)庫(kù)中獲得的所有功能,就是能夠通過(guò)cell()方法使用任意SQL查詢來(lái)獲取單元cell() 。 我們可以使用DB接口的PostgreSQL實(shí)現(xiàn)實(shí)例化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());} }換句話說(shuō),我們正在將依賴項(xiàng)“注入”到新的對(duì)象budget 。
這種“依賴注入”方法的替代方法是讓Budget決定要使用的數(shù)據(jù)庫(kù):
public class Budget {private final DB db = new Postgres("jdbc:postgresql:5740/main");// class methods }這非常臟,導(dǎo)致1)代碼重復(fù),2)無(wú)法重用和3)無(wú)法測(cè)試等。無(wú)需討論原因。 很明顯。
因此,通過(guò)構(gòu)造函數(shù)進(jìn)行依賴注入是一種了不起的技術(shù)。 好吧,甚至沒(méi)有一種技術(shù)。 更像Java和所有其他面向?qū)ο笳Z(yǔ)言的功能。 預(yù)計(jì)幾乎所有對(duì)象都希望封裝一些知識(shí)(也稱為“狀態(tài)”)。 這就是構(gòu)造函數(shù)的用途。
什么是DI容器?
到目前為止,一切都很好,但是這里面是陰暗的一面-依賴項(xiàng)注入容器。 它是這樣工作的(讓我們以Google Guice為例):
import javax.inject.Inject; public class Budget {private final DB db;@Injectpublic Budget(DB data) {this.db = data;}// same methods as above }注意:構(gòu)造函數(shù)帶有@Inject注釋。
然后,我們應(yīng)該在應(yīng)用程序啟動(dòng)時(shí)在某處配置一個(gè)容器:
Injector injector = Guice.createInjector(new AbstractModule() {@Overridepublic void configure() {this.bind(DB.class).toInstance(new Postgres("jdbc:postgresql:5740/main"));}} );一些框架甚至允許我們?cè)赬ML文件中配置注入器。
從現(xiàn)在開(kāi)始,我們將無(wú)法像以前一樣通過(guò)new運(yùn)算符實(shí)例化Budget 。 相反,我們應(yīng)該使用剛剛創(chuàng)建的注射器:
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());} }注入自動(dòng)發(fā)現(xiàn),要實(shí)例化Budget它必須為其構(gòu)造函數(shù)提供一個(gè)參數(shù)。 它將使用我們?cè)谧⑷肫髦袑?shí)例化的Postgres類(lèi)的實(shí)例。
這是使用Guice的正確和推薦的方法。 但是,有一些甚至更暗的模式,這是可能的,但不建議這樣做。 例如,您可以使噴射器成為單例,并直接在Budget類(lèi)中使用它。 這些機(jī)制甚至被DI容器制造商都認(rèn)為是錯(cuò)誤的,因此,讓我們忽略它們,重點(diǎn)關(guān)注推薦的方案。
這個(gè)是來(lái)做什么的?
讓我重申并總結(jié)不正確使用依賴項(xiàng)注入容器的場(chǎng)景:
- 現(xiàn)場(chǎng)注入
- 二傳手注射
- 將注入器作為依賴項(xiàng)
- 使注入器成為全局單例
如果我們將它們?nèi)糠旁谝贿?#xff0c;剩下的就是上面解釋的構(gòu)造函數(shù)注入。 那對(duì)我們有什么幫助? 我們?yōu)槭裁葱枰?#xff1f; 為什么我們不能在應(yīng)用程序的主類(lèi)中使用普通的new ?
如果使用XML,我們創(chuàng)建的容器只是向代碼庫(kù)添加了更多行,甚至添加了更多文件。 它除了增加了復(fù)雜性之外沒(méi)有增加任何東西。 如果遇到以下問(wèn)題,我們應(yīng)該永遠(yuǎn)記住這一點(diǎn):“哪個(gè)數(shù)據(jù)庫(kù)用作預(yù)算的論點(diǎn)?”
正確的方式
現(xiàn)在,讓我向您展示一個(gè)使用new構(gòu)造應(yīng)用程序的真實(shí)示例。 這是我們?cè)趓ultor.com中創(chuàng)建“思維引擎”的方式 (完整的類(lèi)在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)) );印象深刻? 這是真正的對(duì)象組成。 我相信這是應(yīng)該實(shí)例化正確的面向?qū)ο髴?yīng)用程序的方式。
和DI容器? 我認(rèn)為,它們只會(huì)增加不必要的噪音。
相關(guān)文章
您可能還會(huì)發(fā)現(xiàn)以下有趣的帖子:
- Getters / Setters。 邪惡。 期。
- OOP中的反模式
- 避免字符串串聯(lián)
- 對(duì)象應(yīng)該是不可變的
- 為什么NULL是錯(cuò)誤的?
翻譯自: https://www.javacodegeeks.com/2014/10/di-containers-are-code-polluters.html
di容器
總結(jié)
以上是生活随笔為你收集整理的di容器_DI容器是代码污染者的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: jooq sql_用jOOQ用Java编
- 下一篇: 电脑显示器vga线容易坏吗(vga线坏了