浅谈依赖注入之基础篇

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

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

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

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









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值