1、什么是依赖注入 框架
依赖注入的正式定义:ide
依赖注入(Dependency Injection),是这样一个过程:因为某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,因此客户类只定义一个注入点。在程序运行过程当中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,而后将其注入到客户类中,保证客户类的正常运行。函数
2、依赖注入的类别this
1.Setter注入
Setter注入(Setter Injection)是指在客户类中,设置一个服务类接口类型的数据成员,并设置一个Set方法做为注入点,这个Set方法接受一个具体的服务类实例为参数,并将它赋给服务类接口类型的数据成员。spa
下面给出Setter注入的示例代码。设计
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SetterInjection { internal interface IServiceClass { String ServiceInfo(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SetterInjection { internal class ServiceClassA : IServiceClass { public String ServiceInfo() { return "我是ServceClassA"; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SetterInjection { internal class ServiceClassB : IServiceClass { public String ServiceInfo() { return "我是ServceClassB"; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SetterInjection { internal class ClientClass { //注入点 private IServiceClass _serviceImpl; //客户类中的方法,初始化注入点 public void Set_ServiceImpl(IServiceClass serviceImpl) { this._serviceImpl = serviceImpl; } public void ShowInfo() { Console.WriteLine(_serviceImpl.ServiceInfo()); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SetterInjection { class Program { static void Main(string[] args) { IServiceClass serviceA = new ServiceClassA(); IServiceClass serviceB = new ServiceClassB(); ClientClass client = new ClientClass(); client.Set_ServiceImpl(serviceA); client.ShowInfo();//结果:我是ServceClassA client.Set_ServiceImpl(serviceB); client.ShowInfo();//结果:我是ServceClassB Console.ReadLine(); } } }
运行结果以下:3d
2.构造注入code
另一种依赖注入方式,是经过客户类的构造函数,向客户类注入服务类实例。xml
构造注入(Constructor Injection)是指在客户类中,设置一个服务类接口类型的数据成员,并以构造函数为注入点,这个构造函数接受一个具体的服务类实例为参数,并将它赋给服务类接口类型的数据成员。对象
与Setter注入很相似,只是注入点由Setter方法变成了构造方法。这里要注意,因为构造注入只能在实例化客户类时注入一次,因此一点注入,程序运行期间是无法改变一个客户类对象内的服务类实例的。
因为构造注入和Setter注入的IServiceClass,ServiceClassA和ServiceClassB是同样的,因此这里给出另外ClientClass类的示例代码。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConstructorInjection { internal class ClientClass { private IServiceClass _serviceImpl; public ClientClass(IServiceClass serviceImpl) { this._serviceImpl = serviceImpl; } public void ShowInfo() { Console.WriteLine(_serviceImpl.ServiceInfo()); } } }
能够看到,惟一的变化就是构造函数取代了Set_ServiceImpl方法,成为了注入点。
3. 依赖获取
上面提到的注入方式,都是客户类被动接受所依赖的服务类,这也符合“注入”这个词。不过还有一种方法,能够和依赖注入达到相同的目的,就是依赖获取。
依赖获取(Dependency Locate)是指在系统中提供一个获取点,客户类仍然依赖服务类的接口。当客户类须要服务类时,从获取点主动取得指定的服务类,具体的服务类类型由获取点的配置决定。
能够看到,这种方法变被动为主动,使得客户类在须要时主动获取服务类,而将多态性的实现封装到获取点里面。获取点能够有不少种实现,也许最容易想到的就是创建一个Simple Factory做为获取点,客户类传入一个指定字符串,以获取相应服务类实例。若是所依赖的服务类是一系列类,那么依赖获取通常利用Abstract Factory模式构建获取点,而后,将服务类多态性转移到工厂的多态性上,而工厂的类型依赖一个外部配置,如XML文件。
不过,不论使用Simple Factory仍是Abstract Factory,都避免不了判断服务类类型或工厂类型,这样系统中总要有一个地方存在不符合OCP的if…else或switch…case结构,这种缺陷是Simple Factory和Abstract Factory以及依赖获取自己没法消除的,而在某些支持反射的语言中(如C#),经过将反射机制的引入完全解决了这个问题(后面讨论)。
下面给一个具体的例子,如今咱们假设有个程序,既可使用Windows风格外观,又可使用Mac风格外观,而内部业务是同样的。
上图乍看有点复杂,不过若是读者熟悉Abstract Factory模式,应该能很容易看懂,这就是Abstract Factory在实际中的一个应用。这里的Factory Container做为获取点,是一个静态类,它的“Type构造函数”依据外部的XML配置文件,决定实例化哪一个工厂。下面仍是来看示例代码。因为不一样组件的代码是类似的,这里只给出Button组件的示例代码,完整代码请参考文末附上的完整源程序。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DependencyLocate { internal interface IButton { String ShowInfo(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DependencyLocate { internal sealed class WindowsButton : IButton { public String Description { get; private set; } public WindowsButton() { this.Description = "Windows风格按钮"; } public String ShowInfo() { return this.Description; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DependencyLocate { internal sealed class MacButton : IButton { public String Description { get; private set; } public MacButton() { this.Description = " Mac风格按钮"; } public String ShowInfo() { return this.Description; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DependencyLocate { internal interface IFactory { IWindow MakeWindow(); IButton MakeButton(); ITextBox MakeTextBox(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DependencyLocate { internal sealed class WindowsFactory : IFactory { public IWindow MakeWindow() { return new WindowsWindow(); } public IButton MakeButton() { return new WindowsButton(); } public ITextBox MakeTextBox() { return new WindowsTextBox(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DependencyLocate { internal sealed class MacFactory : IFactory { public IWindow MakeWindow() { return new MacWindow(); } public IButton MakeButton() { return new MacButton(); } public ITextBox MakeTextBox() { return new MacTextBox(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; namespace DependencyLocate { internal static class FactoryContainer { public static IFactory factory { get; private set; } static FactoryContainer() { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load("http://www.cnblogs.com/Config.xml"); XmlNode xmlNode = xmlDoc.ChildNodes[1].ChildNodes[0].ChildNodes[0]; if ("Windows" == xmlNode.Value) { factory = new WindowsFactory(); } else if ("Mac" == xmlNode.Value) { factory = new MacFactory(); } else { throw new Exception("Factory Init Error"); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DependencyLocate { class Program { static void Main(string[] args) { IFactory factory = FactoryContainer.factory; IWindow window = factory.MakeWindow(); Console.WriteLine("建立 " + window.ShowInfo()); IButton button = factory.MakeButton(); Console.WriteLine("建立 " + button.ShowInfo()); ITextBox textBox = factory.MakeTextBox(); Console.WriteLine("建立 " + textBox.ShowInfo()); Console.ReadLine(); } } }
这里咱们用XML做为配置文件。配置文件Config.xml以下:
<?xml version="1.0" encoding="utf-8" ?> <config> <factory>Mac</factory> </config>
能够看到,这里咱们将配置设置为Mac风格,编译运行上述代码,运行结果以下:
配置Mac风格后的运行结果
如今,咱们不动程序,仅仅将配置文件中的“Mac”改成Windows,运行后结果以下:
配置为Windows风格后的运行结果
从运行结果看出,咱们仅仅经过修改配置文件,就改变了整个程序的行为(咱们甚至没有从新编译程序),这就是多态性的威力,也是依赖注入效果。
反射与依赖注入
回想上面Dependency Locate的例子,咱们虽然使用了多态性和Abstract Factory,但对OCP贯彻的不够完全。在理解这点前,朋友们必定要注意潜在扩展在哪里,潜在会出现扩展的地方是“新的组件系列”而不是“组件种类”,也就是说,这里咱们假设组件就三种,不会增长新的组件,但可能出现新的外观系列,如须要加一套Ubuntu风格的组件,咱们能够新增UbuntuWindow、UbuntuButton、UbuntuTextBox和UbuntuFactory,并分别实现相应接口,这是符合OCP的,由于这是扩展。但咱们除了修改配置文件,还要无可避免的修改FactoryContainer,须要加一个分支条件,这个地方破坏了OCP。依赖注入自己是没有能力解决这个问题的,但若是语言支持反射机制(Reflection),则这个问题就迎刃而解。
咱们想一想,如今的难点是出在这里:对象最终仍是要经过“new”来实例化,而“new”只能实例化当前已有的类,若是将来有新类添加进来,必须修改代码。若是,咱们能有一种方法,不是经过“new”,而是经过类的名字来实例化对象,那么咱们只要将类的名字做为配置项,就能够实如今不修改代码的状况下,加载将来才出现的类。因此,反射给了语言“预见将来”的能力,使得多态性和依赖注入的威力大增。
下面是引入反射机制后,对上面例子的改进:
能够看出,引入反射机制后,结构简单了不少,一个反射工厂代替了之前的一堆工厂,Factory Container也不须要了。并且之后有新组件系列加入时,反射工厂是不用改变的,只需改变配置文件就能够完成。下面给出反射工厂和配置文件的代码。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Xml; namespace DependencyLocate { internal static class ReflectionFactory { private static String _windowType; private static String _buttonType; private static String _textBoxType; static ReflectionFactory() { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load("http://www.cnblogs.com/Config.xml"); XmlNode xmlNode = xmlDoc.ChildNodes[1].ChildNodes[0]; _windowType = xmlNode.ChildNodes[0].Value; _buttonType = xmlNode.ChildNodes[1].Value; _textBoxType = xmlNode.ChildNodes[2].Value; } public static IWindow MakeWindow() { return Assembly.Load("DependencyLocate").CreateInstance("DependencyLocate." + _windowType) as IWindow; } public static IButton MakeButton() { return Assembly.Load("DependencyLocate").CreateInstance("DependencyLocate." + _buttonType) as IButton; } public static ITextBox MakeTextBox() { return Assembly.Load("DependencyLocate").CreateInstance("DependencyLocate." + _textBoxType) as ITextBox; } } }
配置文件以下:
<?xml version="1.0" encoding="utf-8" ?> <config> <window>MacWindow</window> <button>MacButton</button> <textBox>MacTextBox</textBox> </config>
反射不只能够与Dependency Locate结合,也能够与Setter Injection与Construtor Injection结合。反射机制的引入,下降了依赖注入结构的复杂度,使得依赖注入完全符合OCP,并为通用依赖注入框架(如Spring.NET中的IoC部分、Unity等)的设计提供了可能性。