依赖注入(Dependency Injection)模式

前端之家收集整理的这篇文章主要介绍了依赖注入(Dependency Injection)模式前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1.2 依赖注入模式

依赖注入(Dependency Injection)是一个非常简单的概念@H_404_10@伸手-等待@H_404_10@。(让DIP、IoC滚蛋)

如例程1-5所示,Client依赖于抽象类型(甚至是具体类) IServer,蛋是(1)Client的类体中不创建IServer(子类)的对象@H_404_10@,它不想自己创建IServer对象,而是提供public的构造器Client(IServer)@H_404_10@或设置方法setIServer (IServer)@H_404_10@等(伸手),(2)坐等@H_404_10@外界将初始化后的IServer对象(的引用)传递进来(等待)。

换言之,依赖注入模式是指客户类Client不用自己来初始化它所依赖的成员变量IServer,而是等待某个对象创建IServer的适当的(实现类)的对象并将它赋值给Client的成员变量

依赖注入的意义@H_404_10@,在于保证Client仅仅与(通常是接口或抽象类)IServer耦合@H_404_10@,而不与IServer的子类型耦合,这样的程序符合OCP或依赖于抽象类型原则。

[java] view plain copy
  1. 例程@H_404_10@1@H_404_10@-@H_404_10@5@H_404_10@@H_404_10@@H_404_10@
  2. package@H_404_10@creational;@H_404_10@@H_404_10@
  3. import@H_404_10@java.lang.reflect.*;@H_404_10@//也可以自己搞@H_404_10@@H_404_10@@H_404_10@
  4. import@H_404_10@tool.God;@H_404_10@@H_404_10@
  5. public@H_404_10@@H_404_10@class@H_404_10@Client{@H_404_10@@H_404_10@
  6. private@H_404_10@IServers;@H_404_10@@H_404_10@
  7. /**@H_404_10@@H_404_10@
  8. *依赖注入@H_404_10@@H_404_10@
  9. */@H_404_10@@H_404_10@@H_404_10@
  10. public@H_404_10@Client(IServers){@H_404_10@@H_404_10@
  11. this@H_404_10@.s=s;@H_404_10@@H_404_10@
  12. }@H_404_10@
  13. void@H_404_10@setS(IServers){@H_404_10@@H_404_10@
  14. this@H_404_10@.s=s;@H_404_10@@H_404_10@
  15. }@H_404_10@
  16. static@H_404_10@@H_404_10@void@H_404_10@test(){@H_404_10@@H_404_10@
  17. //使用工具God@H_404_10@@H_404_10@@H_404_10@
  18. IServers=(IServer)God.create("1-5"@H_404_10@);@H_404_10@@H_404_10@
  19. s.m();@H_404_10@
  20. }@H_404_10@

1.2.1 注入的方式

站在Client的角度,Client接受注入@H_404_10@有3种的方式。@H_404_10@

1.构造器注入(Constructor Injection)

客户类Client提供了public的构造器Client (IServer s),等待外界创建IServer的(实现类的)对象后将其引用传递进来/注入。

publicClient(IServer s){

this.s=s;

}

2.Setter注入

Setter注入(Setter Injection)时,客户类Client提供setIServer (IServers),等待外界创建IServer的(实现类的)对象后将其引用传递进来/注入。

publicvoid setIServer (IServer s){

this.s=s;

}

构造器注入与Setter注入是两种常用的方式。构造器注入的优点是创建Client对象时,确保IServer对象被初始化;而采用Setter注入,则创建Client对象后,必须在适当的时候调用setIServer()方法,这就使得程序员可以在灵活的时间(例如希望惰性初始化@H_404_10@),要求外界完成注入@H_404_10@。

3.接口注入

接口注入相当于将Setter注入的setIServer (IServer s) 方法封装到一个专用的接口如InjectIServer中,而Client实现InjectIServer并给出如下的方法体。

@Overridepublic void setIServer (IServer s){

this.s=s;

}

接口注入针对的场景是,有大量Client、Client1等都需要依赖于IServer。

public interface InjectIServer{

publicvoid setIServer (IServer s);

}

代码中使用了[4.1虚域模式]。然而这一方式并不一定被各种依赖注入容器所支持

1.2.2 依赖注入容器/框架

依赖注入模式中,Client等待外界或一个独立的对象——称为注射器@H_404_10@——创建IServer的对象并将它赋值给Client的成员变量。现在考察如何设计注射器。

1.显然,这个注射器不应该是应用系统中的一个类:

copy
    /*@H_404_10@@H_404_10@
  1. classInjection{//注射器不应该是这样的@H_404_10@@H_404_10@
  2. publicstaticvoidtest(){@H_404_10@@H_404_10@
  3. IServers=newServer();@H_404_10@@H_404_10@
  4. Clientc=newClient();@H_404_10@@H_404_10@
  5. c.setIServer(s);//注入@H_404_10@@H_404_10@
  6. c.show();@H_404_10@@H_404_10@
  7. }@H_404_10@@H_404_10@
  8. }*/@H_404_10@@H_404_10@@H_404_10@

这里Injection依赖于Server,如果它和Client在一个包中,就使得早期的目标——Client仅与IServer耦合破坏殆尽,还不如Client直接与具体类Server耦合。

2. 看看我们的工具类tool.God。在任何应用程序App中,都可以用God作为注射器。因而,依赖注入通常意味着某注射器使用反射机制创建对象。换言之,注射器通常使用反射机制创建对象,以作为通用工具。@H_404_10@@H_404_10@

代码中,IServer对象的创建使用了tool.God的静态方法create(),create()不过是一个使用反射+属性配置文件(.properties文件)创建对象的静态工厂。

copy
    package@H_404_10@creational.di;@H_404_10@@H_404_10@
  1. import@H_404_10@tool.God@H_404_10@@H_404_10@
  2. class@H_404_10@App{@H_404_10@//Injection@H_404_10@@H_404_10@@H_404_10@
  3. IServers=(IServer)God.create("1-6"@H_404_10@);@H_404_10@//1-6=creational.di.Server@H_404_10@@H_404_10@@H_404_10@
  4. Clientc=new@H_404_10@Client();@H_404_10@@H_404_10@
  5. c.setIServer(s);//注入@H_404_10@@H_404_10@@H_404_10@
  6. c.show();@H_404_10@
  7. }@H_404_10@

★@H_404_10@单就学习设计模式而言,工具类God已经足够。站在Client的角度,依赖注入模式等待外界创建并传入对象@H_404_10@


3. 但是,比工具类God更为强大的@H_404_10@ 依赖注入容器@H_404_10@@H_404_10@ ,如Spring、PicoContainer等,它们认为@H_404_10@ 使用/依赖关系@H_404_10@ 是面向对象编程的最基本的程序结构,各种各样的使用关系如Client与IServer、C与S等等广泛存在,作为一个依赖注入的工具或框架,希望程序员@H_404_10@ 不再@H_404_10@@H_404_10@ 编写如下代码:@H_404_10@

Client c = new Client();

IServer s = (IServer) God.create("1-6");@H_404_10@

c.setIServer(s);//@H_404_10@注入@H_404_10@

c.show();

设想一下,如果Client依赖很多的类似IServer的服务类型,省略掉上述阴影标识的代码@H_404_10@,将能够节省程序员大量的时间。各种用于依赖注入的专用框架被开发出来如Spring等,它们被称为依赖注入或控制反转容器(DI/IoC Container)

依赖注入模式依赖注入容器、设计依赖注入容器所使用的技术(回调机制或控制反转)是3个东西,虽然密切相关——像爸爸、妈妈和孩子一样密切。

/*请注意,至少到目前为止,我们没有说任何特别的术语——依赖倒置原则DIP控制反转IoC,而此时,我会将依赖注入(Dependency Injection)作为一种设计模式。(也就是说,依赖注入与IoC不是同一个概念)*/
@H_404_10@

Martin Fowler在文章1中挖苦道:【几位轻量级容器的作者曾骄傲地对我说:这些容器非常有用,因为它们实现了控制反转。这样的说辞让我深感迷惑:控制反转是框架所共有的特征,如果仅仅因为使用了控制反转就认为这些轻量级容器与众不同,就好象在说"我的轿车是与众不同的,因为它有四个轮子"。】@H_404_10@

/*[吐槽]在我眼里,目前常见的两个术语@H_225_502@依赖倒置原则DIP@H_404_10@@H_225_502@控制反转IoC@H_404_10@,基本上是没有价值的术语。(因为有回调这个术语足以@H_404_10@),我把作为依赖注入模式和设计依赖注入容器所使用的技术区别开来。更重要的原因,设计依赖注入容器所使用的技术,或者说,设计任一框架所使用的技术就是回调。凭什么要把依赖注入容器称为回调容器或控制反转容器。既然回调是一个常用术语,控制反转作为回调机制的同义词就没有什么价值*/@H_404_10@

下面说明spring的两个用法

1.如同God,仅仅作为一个利用反射+配置文件来创建对象的工具类。

2.按照XML配置文件自动装配——反映出依赖注入容器比God牛x之处。

……

/**

早期网络文章【Ioc容器的革命性优点】写道:“我们知道,在Java基本教程中有一个定律告诉我们:所有的对象都必须创建;或者说:使用对象之前必须创建,但是现在我们可以不必一定遵循这个定律了,我们可以从Ioc容器中直接获得一个对象然后直接使用,无需事先创建它们”。

这是什么话?使用对象之前必须创建,现在我们仍然遵循这个定律!以前咱是一农民Client,自耕自足,自己种粮自己做饭,new Client(new 米());现在发达了,让丫鬟(依赖注入容器)买米,煮饭我吃。没有米我吃啥?

*/

链接

  1. Martin FowlerInversion of Control Containers and the Dependency Injection pattern
  2. InversionOfControl
  3. Shivprasad koirala,12 Jun 2010Design pattern – Inversion of control and Dependency injection好多图。

猜你在找的设计模式相关文章