来源:懒得安分 链接:http://www.cnblogs.com/landeanfen/p/5169163.html
三、使用Unity实现依赖倒置
上面说了那么多,都是在讲依赖倒置的好处,那么在我们的项目中究竟如何具体实现和使用呢?
在介绍依赖倒置具体如何使用之前,我们需要引入IOC容器相关的概念,我们先来看看它们之间的关系。
依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念)。
控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)。这是一个有点不太好理解和解释的概念,通俗地说,就是应用程序本身不负责依赖对象的创建和维护,而是将它交给一个外部容器(比如Unity)来负责,这样控制权就由应用程序转移到了外部IoC 容器,即控制权实现了所谓的反转。例如在类型A中需要使用类型B的实例,而B 实例的创建并不由A 来负责,而是通过外部容器来创建。
依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。园子里面很多博文里面说IOC也叫DI,其实根据博主的理解,DI应该是IOC的具体实现方式,比如我们如何实现控制反转,答案就是通过依赖注入去实现。
IoC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架),自动创建、维护依赖对象。
这些名词是不是有点熟呢?博主之前介绍过MEF,之前使用MEF做过依赖注入,详见MEF实现设计上的“松耦合”(1)。其实严格来讲,MEF不能算一种正式的IOC容器,因为它的主要作用还是用于应用程序扩展,避免生成脆弱的硬依赖项,而不是依赖注入。根据博主的了解以及使用经历,常用的IOC容器有:
当然,还有其他的IOC容器这里就不一一列举。Spring.net是从Java的Spring框架移植过来的,功能之强大我们就不多说了,可是自从它宣布不再更新,博主在使用它的时候就非常慎重了。下面博主还是就Unity这种IOC容器来看看依赖倒置的具体实现。
1、Unity引入
Unity如何引入?我们神奇的Nuget又派上用场了。最新的Unity版本已经到了4.0.1。
安装成功后主要引入了三个dll。
2、Unity常用API
UnityContainer.RegisterType(); UnityContainer.RegisterType(); UnityContainer.RegisterType('keyName'); IEnumerable databases = UnityContainer.ResolveAll(); IT instance = UnityContainer.Resolve(); T instance = UnityContainer.Resolve('keyName'); UnitContainer.RegisterInstance('keyName',new T()); UnityContainer.BuildUp(existingInstance); IUnityContainer childContainer1 = parentContainer.CreateChildContainer();
3.1、默认注册方式
仍然以上面的场景为例说明,我们注入DeviceMML这个实现类。
class Program { private static IUnityContainer container = null; static void Main(string[] args) { RegisterContainer(); var oSpider = container.Resolve(); oSpider.Login(); var bRes = oSpider.Spider(); Console.ReadKey(); } /// /// 代码注入 /// public static void RegisterContainer() { container = new UnityContainer(); container.RegisterType(); //默认注册方式,如果后面再次默认注册会覆盖前面的 } }
运行结果
3.2、带命名方式的注册
上面默认注入的方式中,我们只能注入一种具体的实例,如果我们需要同时注入多个类型的实例呢?看看我们的 RegisterType() 方法有多个重载。
class Program { private static IUnityContainer container = null; static void Main(string[] args) { RegisterContainer(); var oSpider = container.Resolve('TL5'); oSpider.Login(); var bRes = oSpider.Spider(); Console.ReadKey(); } /// /// 代码注入 /// public static void RegisterContainer() { container = new UnityContainer(); container.RegisterType('MML'); //默认注册(无命名),如果后面还有默认注册会覆盖前面的 container.RegisterType('Telnet'); //命名注册 container.RegisterType('TL2'); //命名注册 container.RegisterType('TL5'); //命名注册 } }
运行结果
4、配置文件注入方式示例
在App.config或者Web.config里面加入如下配置:
在代码里面注册配置文件:
namespace ESTM.Spider { class Program { private static IUnityContainer container = null; static void Main(string[] args) { ContainerConfiguration(); var oSpider = container.Resolve('TL5'); oSpider.Login(); var bRes = oSpider.Spider(); Console.ReadKey(); } /// /// 配置文件注入 /// public static void ContainerConfiguration() { container = new UnityContainer(); UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); configuration.Configure(container, 'Spider'); } } }
运行结果:
(1)代码说明
register type='ESTM.Spider.IDevice,ESTM.Spider' mapTo='ESTM.Spider.DeviceMML,ESTM.Spider' name='MML'>register>
节点里面,type对象抽象,mapTo对象具体实例对象,name对象实例的别名。
(2)在app.config里面可以配置多个 container name=”Spider”> 节点,不同的name配置不同的依赖对象。
(3)配置文件注入的灵活之处在于解耦。为什么这么说呢?试想,如果我们的IDevice接口对应着一个接口层,而DeviceMML、DeviceTELNET、DeviceTL2、DeviceTL5等实现类在另外一个实现层里面,我们的UI层(这里对应控制台程序这一层)只需要添加IDevice接口层的引用,不必添加实现层的引用,通过配置文件注入,在运行的时候动态将实现类注入到UI层里面来。这样UI层就对实现层实现了解耦,实现层里面的具体逻辑变化时,UI层里面不必做任何更改。
四、总结
到此,依赖倒置原则的讲解基本结束了。根据博主的理解,设计模式的这些原则是设计模式的理论指导,而设计模式则是这些理论的具体运用。说一千道一万,要想搞懂设计模式,必须先了解设计模式遵循的原则,无论是哪种设计模式都会遵循一种或者多种原则。当然文章可能有理解不当的地方,欢迎大牛们指出
|