一、什么是Ioc

  IoC(Inverse of Control)的字面意思是控制反转,它包括两个内容: 控制、反转

  可以假设这样一个场景:火车运货,不同类型的车厢运送不同类型的货物,板车运送圆木,罐车运送柴油,箱车运送水果。那么对于运送货物这件事,需是列车挂不同的车厢运送货物。显然列车和运送货物之间是有依赖关系的(控制:依赖关系)。我们把列车挂什么样的车厢交给调度中心,而不是交给列车决定,这就形成了依赖反转。

  因为IoC确实不够开门见山,因此业界曾进行了广泛的讨论,最终软件界的泰斗级人物Martin Fowler提出了DI(依赖注入:Dependency Injection)的概念用以代替IoC,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。“依赖注入”这个名词显然比“控制反转”直接明了、易于理解。

依赖注入和控制反转是同一概念吗?

根据上面的讲述,应该能看出来,依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

其实IoC/DI对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC/DI容器来创建并注入它所需要的资源了。

这么小小的一个改变其实是编程思想的一个大进步,这样就有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

       举个例子:有一个闹钟会问早安,当它地理位置定位在中国的时候,它会问候:早安;当在美国的时候,它会问候:Good Morning;那么我们把闹钟看做一个客户,问候看做一个服务,这个闹钟是依赖于服务的。闹钟遵从OCP原则应该有一个SayMorning的注入点,而问候就需要使用策略模式列出。实现这个功能:

public interface ISayMorning
{ void SayMorning();
} public class ChinesePosition:ISayMorning
{
void SayMorning()
{
Console.Writeline("早安!");
}
} public class EnglishPosition:ISayMorning
{
void SayMorning()
{
Console.Writeline("GoodMorning!");
}
} //下面创建客户 public class ClockClient()
{
public ISayMorning SayService{set;}
public Set_Sayservices(ISayMorning sayService)
{
SayService=sayService;
} public SayMorning()
{
SayService.SayMorning();
}
} //主函数中 var clock=new ClockClient();
var saysChinese=new ChinesePosition();
clock.Set_Sayservices(saysChinese);
clock.SayMorning();

以上,闹钟说话依赖于说话服务,说话服务有很多策略(算法),我们将服务注入(DL)客户,将客户的依赖项(服务)反转到对象创建后再根据类别注入选择,这就形成了依赖反转(IOC)。这样做的的好处是策略变化(地区),我们只要新建类就好了

而不用修改已经写好的代码,实现了OCP(设计模式遵循的六大原则之 开闭原则)。

       

二、几个相似相关的概念

依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念)。

控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)。

依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。

IoC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架)。

三、Ioc的类型

IOC的实现有三种方式

构造注入:就是将开头例子中的Setter方式,在创建客户对象的时候,初始化进入。

Setter注入:就是开头的例子。

依赖获取。就是在注入的时候,利用一下虚拟工厂(Abstract Factory),这种适用于服务不仅一种的时候,比如我们赋予闹钟报时功能。

1、构造函数注入

public Class SayHello
{
private IPeople _people;
public SayHello(IPeople p) {
_people=p;
}    public void Say()
{
_people.Say(); } }

2、属性注入

using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest
{
[TestClass]
public class SetterInjectionTest
{
class Client
{
private IWeatherReader reader;
public IWeatherReader Reader
{
get { return reader; }
set { reader = value; }
}
} [TestMethod]
public void Test()
{
IWeatherReader reader = new Assembler<IWeatherReader>().Create();
Client client = new Client();
client.Reader = reader;
Assert.IsNotNull(client.Reader);
}
}
}

也可以写一个Setter方法

3、接口注入

using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest
{
[TestClass]
public class InterfaceInjectionTest
{
interface IClientWithWeatherReader
{
IWeatherReader Reader { get; set;}
} class Client : IClientWithWeatherReader
{
private IWeatherReader reader; #region IClientWithWeatherReader Members
public IWeatherReader Reader
{
get { return reader; }
set { reader = value; }
}
#endregion
} [TestMethod]
public void Test()
{
IWeatherReader reader = new Assembler<IWeatherReader>().Create();
Client client = new Client();
IClientWithWeatherReader clientWithReader = client;
clientWithReader.Reader = reader;
Assert.IsNotNull(clientWithReader.Reader);
}
}
}

4、依赖获取

public interface ISayMorning
{ void SayMorning();
} public class ChinesePosition:ISayMorning
{
void SayMorning()
{
Console.Writeline("早安!");
}
} public class EnglishPosition:ISayMorning
{
void SayMorning()
{
Console.Writeline("GoodMorning!");
}
} public interface IFactory
{
ISayTime MakeTimeSayer();
ISayMoring MakeMorningSayer();
} public class FactoryAmerican:IFactory
{
public ISayTime MakeTimeSayer()
{
return new EnglishPositionTime(); //未实现
}
public ISayMoring MakeMorningSayer()
{
return new EnglishPosition();
}
} //位于中国的工厂
public class FactoryChinese:IFactory
{
public ISayTime MakeTimeSayer()
{
return new ChinesePositionTime(); //未实现
}
public ISayMoring MakeMorningSayer()
{
return new ChinesePosition();
}
} public statics class FactoryContainer
{
static FactoryContainer()
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("Config.xml");
XmlNode xmlNode =xmlDoc.ChildNodes[].ChildNodes[].ChildNodes[]; if ("Chinese" == xmlNode.Value)
{
factory = new FactoryChinese();
}
else if ("American" == xmlNode.Value)
{
factory = new FactoryAmerican();
}
else
{
throw new Exception("Factory Init Error");
}
}
}
} //调用
IFactory factory = FactoryContainer.factory;
IWindow window = factory.SayMorning();

这样,我们可以用过xml文件配置工厂(也就是不同的定位条件)。

四、一个例子

C#

using System;
namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer
{
/// <summary>
/// 抽象的处理对象
/// </summary>
public interface IObjectWithGuid
{
string Guid { get; set;}
}
}

定义需要注入的限制接口,并用一个Attribute管理它 
C#

using System;
namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer
{
/// <summary>
/// 需要注入的用以限制最大数量的接口
/// </summary>
public interface ICapacityConstraint
{
int Max { get;}
} public class CapacityConstraint : ICapacityConstraint
{
private int max;
public CapacityConstraint(){this.max = 0;} // 默认情况下不限制
public CapacityConstraint(int max) { this.max = max; }
public int Max { get { return max; } }
} [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ConstraintAttribute : Attribute
{
private ICapacityConstraint capacity;
public ConstraintAttribute(int max) { this.capacity = new CapacityConstraint(max); }
public ConstraintAttribute() { this.capacity = null; }
public ICapacityConstraint Capacity { get { return capacity; } }
}
}

Assembler上增加通过Attribute注入限制的响应。

using System;
using System.Collections.Generic;
namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer
{
public class Assembler
{
/// <summary>
/// 登记相关类型对“最大容量”属性的使用情况
/// </summary>
private IDictionary<Type, ConstraintAttribute> attributeRegistry = new Dictionary<Type, ConstraintAttribute>(); /// <summary>
/// 登记每个类型(如须受到“最大容量”属性限制的话),实际已经创建的对象数量
/// </summary>
private IDictionary<Type, int> usageRegistry = new Dictionary<Type, int>();
public T Create<T>() where T : IObjectWithGuid, new()
{
ICapacityConstraint constraint = GetAttributeDefinedMax(typeof(T));
if ((constraint == null) || (constraint.Max <= 0)) // max <= 0 代表是不需要限制数量的。
return InternalCreate<T>();
else
{
if (usageRegistry[typeof(T)] < constraint.Max) // 检查是否超出容量限制
{
usageRegistry[typeof(T)]++; // 更新使用情况注册信息
return InternalCreate<T>();
}
else
return default(T);
}
} // helper method
// 直接生成特定实例,并setter 方式注入其guid。
private T InternalCreate<T>()
where T : IObjectWithGuid, new()
{
T result = new T();
result.Guid = Guid.NewGuid().ToString();
return result;
} /// helper method.
// 获取特定类型所定义的最大数量, 同时视情况维护attributeRegistry 和usageRegistry 的注册信息。
private ICapacityConstraint GetAttributeDefinedMax(Type type)
{
ConstraintAttribute attribute = null;
if (!attributeRegistry.TryGetValue(type, out attribute)) //新的待创建的类型
{
// 填充相关类型的“最大容量”属性注册信息
object[] attributes = type.GetCustomAttributes(typeof(ConstraintAttribute), false);
if ((attributes == null) || (attributes.Length <= 0))
attributeRegistry.Add(type, null);
else
{
attribute = (ConstraintAttribute)attributes[0];
attributeRegistry.Add(type, attribute);
usageRegistry.Add(type, 0); // 同时补充该类型的使用情况注册信息
}
}
if (attribute == null)
return null;
else
return attribute.Capacity;
}
}
}

4.2对方案的测试

using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario.Attributer;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest.Attributer
{
[TestClass()]
public class AssemblerTest
{
public abstract class ObjectWithGuidBase : IObjectWithGuid
{
protected string guid;
public virtual string Guid
{
get { return guid; }
set { guid = value; }
}
} [Constraint(2)] // 通过属性注入限制
public class ObjectWithGuidImplA : ObjectWithGuidBase { }
[Constraint(0)] // 通过属性注入限制
public class ObjectWithGuidImplB : ObjectWithGuidBase { }
[Constraint(-5)] // 通过属性注入限制
public class ObjectWithGuidImplC : ObjectWithGuidBase { }
public class ObjectWithGuidImplD : ObjectWithGuidBase { } [TestMethod]
public void Test()
{
Assembler assembler = new Assembler();
for (int i = 0; i < 2; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplA>());
Assert.IsNull(assembler.Create<ObjectWithGuidImplA>()); // 最多两个
for (int i = 0; i < 100; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplB>()); // 不限制
for (int i = 0; i < 100; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplC>()); // 不限制
for (int i = 0; i < 100; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplD>()); // 不限制
}
}
}

设计模式のIOC(控制反转)的更多相关文章

  1. 【设计模式】不同设计模式体现IOC控制反转

    使用过Spring的开发者应该都对IOC控制反转功能有所了解,最开始学习时应该都知道使用依赖注入来实现IOC的功能,本文来介绍使用IOC控制反转思想的几种设计模式. 依赖注入来实现IOC 注入依赖是I ...

  2. 精通android体系架构、mvc、常见的设计模式、控制反转(ioc)

    1.请看某个著名的it公司一则招聘信息的其中一条要求:“熟悉android系统架构及相关技术,1年以上实际android平台开发经验:”,里面非常明确的说道要求熟练android系统架构,这从某种程度 ...

  3. DI依赖注入/IOC控制反转

    DI依赖注入# 啥都不说,直接上代码 <?php class UserController { private $user; function __construct(UserModel $us ...

  4. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

  5. Spring的IOC控制反转和依赖注入-重点-spring核心之一

    IoC:Inverse of Control(控制反转): 读作"反转控制",更好理解,不是什么技术,而是一种设计思想,好比于MVC.就是将原本在程序中手动创建对象的控制权,交由S ...

  6. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  7. .net 温故知新:【7】IOC控制反转,DI依赖注入

    IOC控制反转 大部分应用程序都是这样编写的:编译时依赖关系顺着运行时执行的方向流动,从而生成一个直接依赖项关系图. 也就是说,如果类 A 调用类 B 的方法,类 B 调用 C 类的方法,则在编译时, ...

  8. 回顾Spirng ioc 控制反转

    Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的.结合网上对Spring Ioc的理解,回顾一下自 ...

  9. 谈谈php里的IOC控制反转,DI依赖注入

    理论 发现问题 在深入细节之前,需要确保我们理解"IOC控制反转"和"DI依赖注入"是什么,能够解决什么问题,这些在维基百科中有非常清晰的说明. 控制反转(In ...

随机推荐

  1. 博弈之——SG模板

    很久没搞博弈了.先来写个模板: 现在我们来研究一个看上去似乎更为一般的游戏:给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负.事实上,这个游戏可以 ...

  2. linux查看用户、创建用户、设置密码、修改用户、删除用户命令

    查看用户 /etc/passwd /etc/shadow id alex ' |passwd --stdin alex # 设置密码,不需要交互 [root@localhost ~]# tail -l ...

  3. Laravel 系列入门教程(五)【最适合中国人的 Laravel 教程】

    本文是本系列教程的完结篇,我们将一起给 Article 加入评论功能,让游客在前台页面可以查看.提交.回复评论,并完成后台评论管理功能,可以删除.编辑评论.Article 和评论将使用 Laravel ...

  4. 【20190223】HTTP-知识点整理:HTTPS

    HTTPS:添加了加密及认证机制的HTTP HTTPS 并非是应用层的一种新协议.只是 HTTP 通信接口部分用SSL(Secure Socket Layer)和 TLS(Transport Laye ...

  5. java对程序的简单加密

    File file = new File("oppo.in"); File file1 = new File("main.in"); GregorianCale ...

  6. Javascript 对象 - 数学对象

    数学对象 JavaScript中提供了math对象,math对象包含一些常用的属相和方法.Math对象与Array对象.String对象.Data对象不同,没有构造函数,因此不能创建Math对象.可直 ...

  7. loadrunner 脚本优化-参数化之场景中的参数化取值

    脚本优化-场景中的参数化取值 by:授客 QQ:1033553122   Action() { lr_eval_string("{NewParam}"); lr_eval_stri ...

  8. 自定义控件详解(六):Paint 画笔MaskFilter过滤

    首先看一个API:setMaskFilter(MaskFilter maskfilter): 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等. 以下有两个Ma ...

  9. mongodb数据分组按字符串split

    db.getCollection('users').aggregate([ {$match:{ZWBH:11}}, {$unwind:'$UUID'}, {$project : { PM : { $s ...

  10. WannaCry勒索比特币蠕虫病毒解决方案

    WannaCry ransomware used in widespread attacks all over the world Customer Guidance for WannaCrypt a ...