[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.相关设计模式好文连接

http://gpp.tkchu.me/

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言 unity的框架,除了各大公司自己内部使用的,开源并好用的实际并不是很多,我会慢慢挖掘,依次写出自己的一点见解,错误的地方,望各路大神指正。 一、基本概念 控制反转(Inversion of Control,英文缩写为IOC),我的理解就是,原本A类要获取B类的对象,需要你在A类中自己New一个对象,那么是由A来获取并控制B的对象,IOC就是把对象获取的这个过程交给容器和依赖注入来处理,A类并不知道B的对象是哪里来的,对B对象的控制,由自己变成了其他类,官方一点的概念可以百度,这个还是蛮多的。 二、StrangeIOC基础类型 实际要理解一个框架的类型,还是要自己看源码,这里我只说一下几个重要类型的作用,这个看源码的时候有个印象,也方便理解,而且说这部分的帖子也很多,我就不再赘述了。 1.Context 上下文组件定义程序边界,也就是可以把一个程序定义成多上下文,让代码更加模块化 它提供了程序入口,也算是框架中耦合度最高的地方 2.Binder和Binding 这两个类是这个框架最重要的组成部分 Binding存储了对象的绑定关系,而Binder存储了Binding的对象 3.View和Mediator MVCS中的View层,View只用于显示,也就是View只负责管理UI,Mediator负责界面逻辑,事件响应等 4.Model MVCS中的Model层,负责数据部分 5.Command MVCS中的Control层,负责执行逻辑代码 6.Service MVCS中的Service层,负责与第三方交互,这个Service我理解的,并不是一定指代服务器,也可以是其他的软件,什么都可以,它就是我们程序对外的接口 7.Dispatcher 派发器是框架内通信主线的其中一种,用来派发消息,触发命令,从而进一步解耦 8.Signal 信号是框架内另外一种通信主线,它采用强类型,来绑定信号和命令之间的关系,实现消息响应的触发 9.ReflectionBinder 反射部分,通过binding来获取类的信息,存储在ReflectedClass中 10.injector 注入器,通过反射获取的信息,来实例化请求的对象 --------------------- 作者:蓝天小僧 来源:CSDN 原文:https://blog.csdn.net/zcaixzy5211314/article/details/80876228 版权声明:本文为博主原创文章,转载请附上博文链接!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值