[Unity 设计模式]IOC依赖倒置
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.相关设计模式好文连接
[Unity 设计模式]IOC依赖倒置的更多相关文章
- 北风设计模式课程---依赖倒置原则(Dependency Inversion Principle)
北风设计模式课程---依赖倒置原则(Dependency Inversion Principle) 一.总结 一句话总结: 面向对象技术的根基:依赖倒置原则(Dependency Inversion ...
- ASP.NET 设计模式中依赖倒置原则
依赖倒置原则 A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象. B.抽象不应该依赖于具体,具体应该依赖于抽象. 依赖倒置原则 A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于 ...
- Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截
各位博友金安,首先声明这是一篇转载的博客,原文链接:https://www.cnblogs.com/scottpei/archive/2013/01/08/2851087.html 十年河东,十年河西 ...
- C#软件设计——小话设计模式原则之:依赖倒置原则DIP
前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...
- IoC模式(依赖、依赖倒置、依赖注入、控制反转)
1.依赖 依赖就是有联系,有地方使用到它就是有依赖它,一个系统不可能完全避免依赖.如果你的一个类或者模块在项目中没有用到它,恭喜你,可以从项目中剔除它或者排除它了,因为没有一个地方会依赖它.下面看一个 ...
- 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解
1.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模 ...
- 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解(转)
所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合 ...
- 个人对【依赖倒置(DIP)】、【控制反转(IOC)】、【依赖注入(DI)】浅显理解
一.依赖倒置(Dependency Inversion Principle) 依赖倒置是面向对象设计领域的一种软件设计原则.(其他的设计原则还有:单一职责原则.开放封闭原则.里式替换原则.接口分离原则 ...
- 【转载】浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)
原文地址 http://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...
随机推荐
- Win10 “此环境变量太大。此对话框允许将值设置为最长2047个字符。" 解决方法。
打开注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment 双击右边的 Path (RE ...
- First Project -用函数写的ATM+购物商城程序
作业需求:模拟实现一个ATM + 购物商城程序 额度15000或自定义 实现购物商城,买东西加入 购物车,调用信用卡接口结账 可以提现,手续费5% 每月22号出账单,每月10号为还款日,过期未还,按欠 ...
- CAD设置背景图片
把图片作为背景图片可见但是不能编辑操作. 主要用到函数说明: _DMxDrawX::DrawImageToBackground 绘光栅图到背景.详细说明如下: 参数 说明 BSTR sFileName ...
- 并发和多线程(三)--并发容器J.U.C和lock简介
AQS: 是AbstractQueuedSynchronizer的简称,JUC的核心 底层是sync queue双向链表,还可能有condition queue单向链表,使用Node实现FIFO队列, ...
- Word 格式优化
Word 格式优化. Word 支持 VBA 意味着,可以编程实现自己想要的格式拓展. Word 代码布局
- fork 系统调用
对自己知识储备的感觉就是过于肤浅,很多东西知其名后就不了了之 此系列博客将记录进程分析的学习过程,希望能够多些深度 提到进程,最容易的想到就是fork系统调用,比较好和快速的找到的fork的相关信息就 ...
- linux初步学习有感
经过了一段时间对linux的接触,从最开始接触到的deepin到后来我最喜欢的KaliLinux,感受到了这个我曾经并不了解的操作系统的独特魅力. 我是到了大学才知道linux这个系统的,但是在小时候 ...
- buf.readUInt16BE()
buf.readUInt16BE(offset[, noAssert]) buf.readUInt16LE(offset[, noAssert]) offset {Number} 0 <= of ...
- 【转】精选十二款餐饮、快递、票务行业微信小程序源码demo推荐
微信小程序的初衷是为了线下实体业服务的,必须有实体相结合才能显示小程序的魅力.个人认为微信小程序对于餐饮业和快递业这样业务比较单一的行业比较有市场,故整理推荐12款餐饮业和快递业微信小程序源码demo ...
- 洛谷 1894 [USACO4.2]完美的牛栏The Perfect Stall
[题解] 其实是个二分图最大匹配的模板题,直接上匈牙利算法就好了. #include<cstdio> #include<algorithm> #define N 1010 #d ...