浅谈依赖注入之基础篇

来源:互联网 发布:动态墙纸软件 编辑:程序博客网 时间:2024/06/06 07:49

鄙人才疏学浅,若有不到之处,请多指教。

最近对依赖注入突然产生了浓厚的兴趣,所以决定搜索一些资料然后自己消化一下,并稍作整理,一来以后能够翻阅温习,二来能够为初识“依赖注入”的童鞋做一些参考。闲话少说,开始正题。

1.定义

说道“依赖注入”会联系到令一个词“控制反转”,其实他们说的都是一回事儿,以下摘自百度知道对“依赖注入的”定义:

控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题。 控制反转还有一个名字叫做依赖注入(Dependency Injection)。简称DI。

通过定义我们也能理解到 IOC不是什么技术,与GOF一样,是一种设计模式。其目的就是降低耦合度。

2.理解

既然为“依赖注入”,那我们先搞懂什么是依赖。

简单的理解,就是一个类A使用到了令一个类B,而这种使用关系是具有偶然行的、临时性的、非常脆弱的,但是B类的变化会影响到A;也就是B作为参数被A在某个方法中使用。

现在编程比较流行的是接口驱动(面向接口编程),什么时候用接口呢?比如说B类会有多种的形态存在,但是基本行为都一样。那么B类就可以抽象出来一个接口IB,这样A就依赖于IB,也就是A依赖于接口。但是接口只是方法的签名,那么具体的实现还得是继承了IB的B1或B2,也就是所A应该能够调用具体的B1或者B2的方法,最终依赖的是B1或者B2。

代码如下:

//定义接口IB    public interface IB    {        void Method();    }    public class B1 : IB    {        public void Method()        {            Console.WriteLine("B1的Method");        }    }    public class B2 : IB    {        public void Method()        {            Console.WriteLine("B2的Method");        }    }    public class A    {        public IB instance;        public A(IB b)        {            instance = b;//实例化IB        }        public void Method()        {            //调用IB的具体的实例的方法            instance.Method();        }    }

这里描述了A和IB之间的依赖关系,在A类的构造函数中需要一个IB类型的参数,并且赋值给IB类型的instance属性,这样在A的方法中可以调用instance的方法。

客户端调用:

    internal class Program    {        private static void Main(string[] args)        {            IB b1 = new B1();            A a = new A(b1);//将b1注入到了A中            a.Method();        }    }
从客户端调用的代码中不难看出,依赖注入的基本原理。通过A的构造函数传递IB的实例的过程就是依赖注入。当然我们也可以不借助构造函数,也可以给IB类型的属性构造访问器,在客户端直接访问属性进行设置。这样也完成了注入操作。

到此我们可以重新理解依赖注入的定义,因为之前的定义很模糊不够具体。

依赖注入的正式定义:

依赖注入(Dependency Injection),是这样一个过程:由于某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行。

3.依赖注入的类别

依赖注入的方式不同,之前也提到了,下边就正式定义一下依赖注入的类别。

3.1 构造注入

顾名思义,是通过客户类(A类)的构造函数,像客户类注入服务类(IB类)实例。

构造注入(Constructor Injection)是指在客户类中,设置一个服务类接口类型的数据成员,并以构造函数为注入点,这个构造函数接受一个具体的服务类实例为参数,并将它赋给服务类接口类型的数据成员。(上边的代码就用的构造注入)

3.2  Setter注入

Setter注入(Setter Injection)是指在客户类中,设置一个服务类接口类型的数据成员,并设置一个Set方法作为注入点,这个Set方法接受一个具体的服务类实例为参数,并将它赋给服务类接口类型的数据成员。

也就是我刚刚上边提到的 定义属性访问器来赋值给IB类型的属性的方式进行注入。

代码如下:

    //定义接口IB(服务类)    public interface IB    {        void Method();    }    public class B1 : IB    {        public void Method()        {            Console.WriteLine("B1的Method");        }    }    public class B2 : IB    {        public void Method()        {            Console.WriteLine("B2的Method");        }    }    //客户类    public class A    {        private IB instance;        public A()        {        }        //注入点        public IB Set_instance        {            set { instance = value; }        }        public void Method()        {            //调用IB的具体的实例的方法            instance.Method();        }    }    internal class Program    {        //客户端调用        private static void Main(string[] args)        {            IB b1 = new B1();            A a = new A();            a.Set_instance = b1;//将b1注入到了A中            a.Method();        }    }
3.3 依赖获取

依赖获取(Dependency Locate)是指在系统中提供一个获取点,客户类仍然依赖服务类的接口。当客户类需要服务类时,从获取点主动取得指定的服务类,具体的服务类类型由获取点的配置决定。

依赖获取变被动为主动

具体代码如下(摘自张洋http://www.cnblogs.com/leoo2sk/archive/2009/06/17/1504693.html)

下面给一个具体的例子,现在我们假设有个程序,既可以使用Windows风格外观,又可以使用Mac风格外观,而内部业务是一样的。

图1依赖获取示意

上图乍看有点复杂,不过如果读者熟悉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风格,编译运行上述代码,运行结果如下:

图2 配置Mac风格后的运行结果

当然我们也可以利用反射来简化工厂中的创建实例的方法。具体的不在这里说了。









原创粉丝点击