1.前言

最近在看《游戏开发与设计模式》一书,看到控制反转设计模式,作者说:上层模块不应该依赖于下层模块,上层模块和下层模块都应该依赖于接口,这样能减少耦合。然后附带举了个例子,我觉得特别好,就是一台计算机是属于上层模块,里面硬盘属于下层模块,计算机依赖于硬盘,硬盘是计算机的基本组成部件之一。这里提到依赖一词,下面就详细谈谈依赖。

2.依赖

依赖就是一种联系关系,人对人的依赖那是一种羁绊关系。再拿上面的计算机举例,华硕是我们都耳熟能详的计算机厂商,西部数据和希捷都是硬盘厂商,如果说华硕依赖于某一个具体型号的硬盘,那么华硕同一款型号的电脑那得生产多少台内置硬盘不同的型号的电脑。假设华硕厂商喜欢用西部数据的硬盘,那好假如西部数据倒闭或者硬盘升级,那么华硕厂商是不是会造成连锁反应,华硕已经生产的电脑要重新回炉重造或者说要被迫改成使用其他希捷厂商的电脑。造成这种问题的原因就是华硕对西部数据有严重的依赖关系,解决方法就是硬盘厂商生产硬盘的时候遵循硬盘接口SATA接口协议,然后计算机厂商也遵循这种接口协议,这就是我们看到的PC具有好多接口例如我们熟知的USB接口,USB是更通用的接口,无论鼠标、键盘还是优盘都具备这样的接口,所以只要遵循USB接口或者ASTA接口的设备都能用用在任何一个PC计算机上。我们如果写代码的话,也能这样设计我们的代码模块,那么模块跟模块之间耦合度就很低了。下面我们来看一个简单的例子:

 public class FlashDisk
{
public string TypeName { get; set; }
public void TransportData()
{
Console.WriteLine(string.Format("读取{0}硬盘的数据"));
} public FlashDisk(string name)
{
TypeName = name;
}
} public class HardDisk
{
public string TypeName { get; set; }
public void TransportData()
{
Console.WriteLine(string.Format("读取{0}硬盘的数据"));
} public HardDisk(string name)
{
TypeName = name;
}
}
public class Computer
{
public Computer(string name)
{
TypeName = name;
}
public void ReadHardDiskData(HardDisk m_HardDisk)
{
m_HardDisk.TransportData();
} public void ReadFlashDiskData(FlashDisk m_FlashDisk)
{
m_FlashDisk.TransportData();
}
}
static void ReadData()
{
HardDisk hardDisk = new HardDisk("西部数据");
FlashDisk flashDisk = new FlashDisk("闪迪");
Computer pc = new Computer("联想");
pc.ReadHardDiskData(hardDisk);
pc.ReadFlashDiskData(flashDisk);
}

上面是一个简单的示例,想要实现传输数据的功能,用户的操作一个PC来读取某个硬盘或者优盘才能实现。先看看依赖关系: 
- Computer依赖HardWare 
- ReadData操作依赖Computer 
- ReadData操作依赖HardDisk/FlashDisk

3.依赖倒置

如果我们这时候用不用的电脑比如联想来读取金斯顿优盘的数据,我们就要抽象出来减少耦合。 
耦合就是依赖,如果依赖过于严重就会牵一发而动全身,所谓城门失火,殃及池鱼。依赖关系越少,维护就越容易,所以必须要减少依赖。 
幸亏Robert Martin提出面向对象设计原则-依赖倒置原则: 
- 上层模块不应该依赖于下层模块,他们共同依赖于一个抽象。 
- 抽象不依赖于具体,具体依赖于抽象。 
理解: 
- 上层模块就是使用者,这里的PC使用优盘、硬盘,而后者自然就是被使用者就是下层模块。他们应该都共同依赖于抽象——接口。 
- 面向对象编程时需要面向抽象或者面向接口编程,抽象或者接口一般比较稳定,接口不要依赖于具体的对象。

根据控制反转的原则我们来重新设计上的实现: 
- 优盘或者硬盘都要遵循一个接口协议,这里我们就定义ISATA和IUSB协议, 
- 数据传输接口ISATA和IUSB都要遵循数据传输协议我们就定义ITransportData 
- ReadData操作依赖PC,就让ReadData和HardDisk/FlashDisk都依赖ISATA和IUSB协议,于是优化过的代码就出来了

 using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public interface ITransportData
{
void TransPortData();
} public interface IUSB : ITransportData
{
string TypenName { get; set; }
} public interface ISATA : ITransportData
{
string TypenName { get; set; }
} public class UFlashDisk : IUSB
{
public string TypenName { get; set; } public UFlashDisk(string typeName)
{
TypenName = typeName;
}
public void TransPortData()
{
UnityEngine.Debug.Log(string.Format("优盘型号:{0}传输数据", TypenName));
}
} public class HardDisk : ISATA
{
public string TypenName { get; set; } public HardDisk(string typeName)
{
TypenName = typeName;
} public void TransPortData()
{
UnityEngine.Debug.Log(string.Format("硬盘型号:{0}传输数据", TypenName));
}
} public class Computer
{
ISATA m_HardDisk; public void SetHardWare(ISATA hardDisk)
{
m_HardDisk = hardDisk;
} public void ReadData()
{
if (m_HardDisk == null)
throw new Exception();
m_HardDisk.TransPortData();
} public void UseUFlashDisk(IUSB flashDisk)
{
flashDisk.TransPortData();
}
} public class IOCDemo : MonoBehaviour
{
void Start()
{
ISATA hardDisk = new HardDisk("西部数据硬盘");
IUSB uFlashDisk = new UFlashDisk("闪迪优盘");
var pc = new Computer();
pc.SetHardWare(hardDisk);
pc.ReadData();
pc.UseUFlashDisk(uFlashDisk);
pc.UseUFlashDisk(new UFlashDisk("西门子优盘"));
}
}

4.控制反转(IOC)

现实生活中,我们想要读取数据,无论我们用什么电脑,文件数据在哪个优盘里,这都取决于我们用户自己。 
上面基本实现了隔离,具体的电脑跟优盘隔离,具体的优盘、硬盘跟接口有关系,如果我们对象在代码里面写死,假如就用华硕电脑读取闪迪优盘,这样又不灵活了,如果需求是我用联想电脑去读取金斯顿优盘,我们是不是又要改代码。这样我们就可以把决定权交给我们配置的人,游戏开发中这类人就称之为策划,我们就把权利给他们,让他们想用什么电脑读取什么优盘就让他们来配表实现,然后程序写一个读表生成具体对象的读取数据的操作。 
这样控制权交给了配表的文件,这种就是我们这里要说的控制反转。

 public class Computer
{
public string Name { get; set; }
public Computer(string name)
{
Name = name;
} ISATA m_HardDisk; public void SetHardWare(ISATA hardDisk)
{
m_HardDisk = hardDisk;
} public void ReadData()
{
if (m_HardDisk == null)
throw new Exception();
m_HardDisk.TransPortData();
} public void UseUFlashDisk(IUSB flashDisk)
{
flashDisk.TransPortData();
}
} var pc = new Computer(“读取配置表”);
pc.SetHardWare(new UFlashDisk(“读取配置表”));
pc.ReadData();

控制反转IOC是Inversion of control的缩写,说将对象进行转移,转移到第三方,比如交给了配表文件。它就像一个对象创建工程,想要什么对象就配置什么对象,这样原先的依赖关系没有了,都变成IOC容器依赖了。

5.依赖注入(ID)

上面说的控制反转是一个思想概念,要看具体的实现,配表也是一种实现方式。依赖注入提出了具体的思想。 
依赖注入DI是Dependency Injection缩写,它提出了“哪些东东的控制权被反转了,被转移了?”,它也给出了答案:“依赖对象的创建获得被反转”。 
所谓依赖注入,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。 
上面SetHardWare其实就是运行时候依赖注入,将具体的依赖对象传递进去,传递的切入点可以是方法,可以是属性,可以是构造函数。

6.小结

通过一个小例子来由浅入深讲解IOC模式,看完之后记得自己在敲一遍哦,这样感悟才深刻!

7.工程下载

https://git.oschina.net/dingxiaowei/UnityIOC.git

8.相关设计模式好文连接

http://gpp.tkchu.me/

[Unity 设计模式]IOC依赖倒置的更多相关文章

  1. 北风设计模式课程---依赖倒置原则(Dependency Inversion Principle)

    北风设计模式课程---依赖倒置原则(Dependency Inversion Principle) 一.总结 一句话总结: 面向对象技术的根基:依赖倒置原则(Dependency Inversion ...

  2. ASP.NET 设计模式中依赖倒置原则

    依赖倒置原则 A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象. B.抽象不应该依赖于具体,具体应该依赖于抽象. 依赖倒置原则 A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于 ...

  3. Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截

    各位博友金安,首先声明这是一篇转载的博客,原文链接:https://www.cnblogs.com/scottpei/archive/2013/01/08/2851087.html 十年河东,十年河西 ...

  4. C#软件设计——小话设计模式原则之:依赖倒置原则DIP

    前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...

  5. IoC模式(依赖、依赖倒置、依赖注入、控制反转)

    1.依赖 依赖就是有联系,有地方使用到它就是有依赖它,一个系统不可能完全避免依赖.如果你的一个类或者模块在项目中没有用到它,恭喜你,可以从项目中剔除它或者排除它了,因为没有一个地方会依赖它.下面看一个 ...

  6. 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解

    1.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模 ...

  7. 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解(转)

    所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合 ...

  8. 个人对【依赖倒置(DIP)】、【控制反转(IOC)】、【依赖注入(DI)】浅显理解

    一.依赖倒置(Dependency Inversion Principle) 依赖倒置是面向对象设计领域的一种软件设计原则.(其他的设计原则还有:单一职责原则.开放封闭原则.里式替换原则.接口分离原则 ...

  9. 【转载】浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文地址 http://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

随机推荐

  1. 在Resource中使用x:Bind

    Build2015上,MS热情高涨的演示了x:Bind,一种新的Binding方式,新的方式有如下优点: 1更好的性能(内存占用,CPU占用) 2BuildTime的Binding 具体在Channe ...

  2. Codeforces_761_E_(dfs)

    E. Dasha and Puzzle time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  3. js数组的处理

    //重写Array中的indexOf方法,获取数组中指定值的元素的索引 Array.prototype.indexOf = function (val) { for (var i = 0; i < ...

  4. Queueingconsumer 找不到

    springboot从1.5.9升级到2.0.0,queueingconsumer报错没有这个类,改为使用 DefaultConsumer

  5. SIGFPE能处理上下溢吗?

    SIGFPE可以报告算术异常.包括上下溢.除零等. C标准库说: 各种实现报告的异常类型区别也很大,但几乎没有实现会报告整型溢出. 真的吗?我测试了一下,发现在GCC上,整型.浮点上下溢都不会报告,但 ...

  6. xmpp登录(2)

    XMPP中常用对象们: XMPPStream:xmpp基础服务类 XMPPRoster:好友列表类 XMPPRosterCoreDataStorage:好友列表(用户账号)在core data中的操作 ...

  7. Servlet监听器的使用

    Servlet监听器的使用 制作人:全心全意 在Servlet技术中已经定义了一些事件,并且可以针对这些事件来编写相关的事件监听器,从而对事件做出相应的处理.例如,想要在Web应用程序启动和关闭时来执 ...

  8. L2-012. 关于堆的判断(STL中heap)

    L2-012. 关于堆的判断   将一系列给定数字顺序插入一个初始为空的小顶堆H[].随后判断一系列相关命题是否为真.命题分下列几种: “x is the root”:x是根结点: “x and y ...

  9. .NET-高并发及限流方案

    前言:高并发对我们来说应该都不陌生,特别想淘宝秒杀,竞价等等,使用的非常多,如何在高并发的情况下,使用限流,保证业务的进行呢.以下是一个实例,不喜勿喷! 总体思路: 1.  用一个环形来代表通过的请求 ...

  10. python整数转ASCII码

    # *-* coding:utf-8 *-* import binascii data = [1441465642, 251096121, -870437532, -944322827, 647240 ...