浅谈依赖注入之基础篇

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

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

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

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风格后的运行结果

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

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