概述
在进行WPF开发时,我遇到了Unity Container、IoC、Dependency Injection等。当时我陷入到困惑的思考总,为什么要使用这些。当后来我逐渐的了解这些技术的优点后,我开始意识到了我们实际上是需要他们的。
本文,我来解释DI和IoC的需求和使用情况,本文分为5个部分。
1. 依赖倒置原则
2. 控制反转和控制反转容器(IoC Container)
3. 自定义控制反转容器(IoC Container)
4. 自定义支持生命周期的控制反转容器(IoC Container)
5. 利用Microsoft Unity的依赖注入(Dependency Injection)
前提条件
最好是最如下内容有所了解
开闭原则(Open/closed principle)
接口分离原则
依赖倒置原则(DIP)
DIP是SOLID原则中的一个,他们由Robert Martin于1992年提出。
S – Single responsibility principle(单一职责原则)
O – Open/closed principle(开闭原则)
L – Liskov substitution principle(里氏代换原则)
I – Interface segregation principle(接口分离原则)
D – Dependency inversion principle(依赖倒置原则)
C. Robert Matrin的依赖倒置原则内容如下:
a). 高层次的模块不应该依赖低层次的模块,他们都应该依赖于抽象。
b). 抽象不应该依赖于具体,具体应该依赖于抽象。
DIP指的就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
图 1
如图1所示,“Copy”程序(高层次的模块)会调用“Read Keyboard”和“Write Printer”。因此“Copy”依赖“Read Keyboard”和“Write Printer”,并且存在紧耦合。
public class Copy { publicvoidDoWork() { ReadKeyboardreader =new ReadKeyboard(); WritePrinterwriter =new WritePrinter(); string data= reader.ReadFromKeyboard(); writer.WriteToPrinter(data); } }
上述代码看起来还不错,但是当我们需要追加更多的Reader或者Writer对象时,在上述代码结构下,我们需要修改“copy”程序,增加创建新Reader或Writer对象,并且要追加条件判断语句,来选择用哪个Reader或Writer来执行操作。但是这么做的话,违背了面向对象的开闭原则。
例如,我们扩展“Copy”程序,如图2,此时我们的“copy”程序可以从扫描器读数据并能够将数据写入到磁盘。
图 2
在这种情况下,我们需要调整我们的“copy”程序
public class Copy { publicvoidDoWork() { string data=string.Empty; switch(readerType) { case"keyboard": ReadKeyboardreader = newReadKeyboard(); data =reader.ReadFromKeyboard(); break; case"scanner": ReadScannerreader2 = newReadScanner(); data =reader2.ReadFromScanner(); break; } switch(writerType) { case"printer": WritePrinterwriter = newWritePrinter(); writer.WriteToPrinter(data); break; case"flashdisk": WriteFlashDiskwriter2 = newWriteFlashDisk(); writer2.WriteToFlashDisk(data); break; } } privatestringreaderType; privatestringwriterType; }根据上面的场景,如果需要继续追加新的Reader和Writer,我们就需要修正copy程序,因此copy严重的依赖具体的Reader和Writer对象。
为了解决上述问题,我们可以调整我们的“copy”程序,使其依赖抽象的接口而不是具体的实现。下图解释了依赖倒置。
图 3
图 4
在图3、4中,“Copy”程序依赖两个抽象的接口IReader和IWriter,这样低级别的组件就被抽象成了接口。
如下例子,在上述的图中,可以看出,类“ReadKeyboard”派生自接口IReader,类“WritePrinter”派生自接口IWriter,因此“Copy”程序调用IReader和IWriter就执行具体的操作了。因此如果我们想要追加更多的底层组件,例如“Scanner”和“FlashDisk”,我们可以创建派生自“IReader”和“IWriter”的新类即可。
public interface IReader { stringRead(); } public interface IWriter { voidWrite(string data); } public class ReadKeyboard :IReader { publicstringRead() { // code to read from keyboardand return as string returnstring.Empty; } } public class ReadScanner :IReader { publicstringRead() { // code to read from scannerand return as string returnstring.Empty; } } public class WritePrinter :IWriter { publicvoidWrite(string data) { // code to write to theprinter } } public class WriteFlashDisk :IWriter { publicvoidWrite(string data) { // code to write to the flashdisk } } public class Copy { privatestring_readerType; privatestring_writerType; public Copy(stringreaderType,string writerType) { _readerType = readerType; _writerType = writerType; } publicvoidDoWork() { IReaderreader =null; IWriterwriter =null; string data; switch(readerType) { case"keyboard": reader = new ReadKeyboard(); break; case"scanner": reader = new ReadScanner(); break; } switch(writerType) { case"printer": writer = new WritePrinter(); break; case"flashdisk": writer = new WriteFlashDisk(); break; } data = reader.Read(); writer.Write(data); } }
在这种情况下,虽然具体的读写操作已经被抽象出来的,但是仍然存在高层模块依赖底层模块的现象,因为我们在高层模块中明确的实例化了具体的底层类对象。因此这种情况下,当追加新的类时,代码中的swith、case处仍然需要不断的维护。因此上述的修改方案并未完全的满足DIP原则。
为了彻底的移除依赖,我们需要在高层组件外面来创建依赖对象,并且依赖对象中存在某种机制能够传递到依赖模块中。
现在新的问题来了,如何实现依赖反转(Dependency Inversion)
其中一个方法是控制反转(Inversion ofControl, IoC),相当于如下代码:
public class Copy { publicvoidDoWork() { IReaderreader = serviceLocator.GetReader(); IWriterwriter = serviceLocator.GetWriter(); string data= reader.Read(); writer.Write(data); } }上述代码替代了实例化Reader和Writer对象的代码,上述代码已经将创建Reader和Writer对象的处理转移到servicelocator中。从而使得“copy”程序不必因为底层组件的增减而修改了。
依赖注入(DependencyInjection)是实现控制反转的一种机制,在下一部分我会介绍什么是控制反转(Inversionof Control, IoC)及利用不同的机制实现依赖倒置原则(其中依赖注入是其中的一种办法)。
小结
本文介绍了依赖倒置原则,及其在真实的使用场景。在后面的文章中我会介绍控制反转(Inversionof Control,IoC)和依赖注入(DependencyInjection,DI)。
原文地址
http://www.codeproject.com/Articles/465173/Dependency-Inversion-Principle-IoC-Container-Depen