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. 字符集编码---3 Windows BOM

    Windows平台下存储Unicode格式的文件时,会在文件头插入2到3字节的文件头.这个文件头就是BOM(Byte Order Marks). 这个文件头在Unicode中,无对应符号.所以不必担心 ...

  2. 更新html技术比较

    document.write() document对象的write方法可以很简单的向页面的源代码中添加内容,不过不推荐使用. 优点:可以快速简单的让初学者理解如何向页面添加内容: 缺点: 只有页面初始 ...

  3. HiveServer2后台运行

    nohup hive --service hiveserver2 & 或者直接: nohup hiveserver2 &

  4. vi 命令学习(二)

    [选中文本(可视模式)] v 可视模式 从光标位置开始按正常模式选择文本 V 可视行模式 选中光标经过的完整行 ctrl + v 可视块模式 垂直方向选中文本 [ 撤销和恢复撤销] u undo 撤销 ...

  5. UVALIVE6886 Golf Bot (FFT)

    题意:打高尔夫 给你n个距离表示你一次可以把球打远的距离 然后对于m个询问 问能否在两杆内把球打进洞 题解:平方一下就好 注意一下x0的系数为1表示打一杆 才发现数组应该开MAXN * 4 之前写的题 ...

  6. 【原】thinkphp实现存储session至redis

    Thinkphp\Library\Think\Session\Driver中新建redis缓存文件:Redis.class.php Thinkphp\Common\function.php 中 fun ...

  7. document.write() 和 document.writeln区别

    document.write() 和 document.writeln 都是JavaScript向客户端写入的方法,writeln是以行方式输出的,但并不是指页面实际效果中的换行,两种方法在查看源代码 ...

  8. 小程序wx:key = “{{*this}}”报错

    解决方案:改为 wx:key = "*this"

  9. 【webpack插件使用】在开发中快速掌握并使用Webpack构建web应用程序

    1.webpack-dev-server插件的基本使用 入门程序 const path = require('path'); // 导出一个Webpack的配置对象(通过node中的模块操作,向外暴露 ...

  10. Bullet:ORACLE Using SQL Plan Management(一)

    SQL Plan Management如何工作? 当一个SQL硬解析时,基于成本的优化器CBO会生成多个执行计划,并从这些执行计划中选择一个优化器认为最低成本的执行计划. 如果SQL plan bas ...