C# 设计模式(七)外观模式(unity演示)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lxt610/article/details/81283652


1、引言

  在软件开发过程中,客户端程序经常会与复杂系统的内部子系统进行耦合,从而导致客户端程序随着子系统的变化而变化,然而为了将复杂系统的内部子系统与客户端之间的依赖解耦,从而就有了外观模式,也称作 ”门面“模式。下面就具体介绍下外观模式。

2、外观模式详细介绍

2.1、定义

  • 外观模式(Facade Pattern):
      外观模式提供了一个统一的接口,用来访问子系统中的一群接口。

外观定义了一个高层接口,让子系统更容易使用。使用外观模式时,我们创建了一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以直接通过外观类来调用内部子系统中方法,从而外观模式让客户和子系统之间避免了紧耦合。

如图:
这里写图片描述

2.2、作用

该模式的作用如下:

  • 实现客户类与子系统类的松耦合
  • 降低原有系统的复杂度
  • 提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。
  1. 引入外观角色之后,用户只需要与外观角色交互;
  2. 用户与子系统之间的复杂逻辑关系由外观角色来实现;

2.3、解决的问题

该模式解决了一下问题:

  1. 避免了系统与系统之间的高耦合度
  2. 使得复杂的子系统用法变得简单

2.4、模式的原理

下面是该模式的UML类图:
这里写图片描述

通过上图我们不难发现,在外观模式中只有两个角色:

  • 门面(Facade)角色:客户端调用这个角色的方法。该角色知道相关的一个或多个子系统的功能和责任,该角色会将从客户端发来的请求委派带相应的子系统中去。
  • 子系统(subsystem)角色:可以同时包含一个或多个子系统。每个子系统都不是一个单独的类,而是一个类的集合。每个子系统都可以被客户端直接调用或被门面角色调用。对于子系统而言,门面仅仅是另外一个客户端,子系统并不知道门面的存在。

2.5、类图实现

下面是详细代码:

子系统:

/// <summary>
/// 子系统
/// </summary>
class SubSystemOne
{
    public void MethodOne()
    {
        Console.WriteLine("子系统一,方法一");
    }
}

/// <summary>
/// 子系统
/// </summary>
class SubSystemTwo
{
    public void MethodTwo()
    {
        Console.WriteLine("子系统二,方法二");
    }
}

/// <summary>
/// 子系统
/// </summary>
class SubSystemThree
{
    public void MethodThree()
    {
        Console.WriteLine("子系统三,方法三");
    }
}

/// <summary>
/// 子系统
/// </summary>
class SubSystemFour
{
    public void MethodFour()
    {
        Console.WriteLine("子系统四,方法四");
    }
}

门面:

class Facade
{
    SubSystemOne one;
    SubSystemTwo two;
    SubSystemThree three;
    SubSystemFour four;

    public Facade()
    {
        one = new SubSystemOne();
        two = new SubSystemTwo();
        three = new SubSystemThree();
        four = new SubSystemFour();
    }

    public void MethodA()
    {
        Console.WriteLine("\n方法组A() ----");
        one.MethodOne();
        two.MethodTwo();
        four.MethodFour();
    }

    public void MethodB()
    {
        Console.WriteLine("\n方法组B() ----");
        two.MethodTwo();
        three.MethodThree();
    }
}

测试一下:

Facade facade = new Facade();

facade.MethodA();
facade.MethodB();

测试结果如下:

方法组A() ----
子系统一,方法一
子系统二,方法二
子系统四,方法四

方法组B() ----
子系统二,方法二
子系统三,方法三

2.6、C#举例

  • 情景
      小成的爷爷已经80岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;

  • 问题
      行动不方便,走过去关闭那么多电器很麻烦,代码如下:

子系统(电器类):

//灯类
public class SubSystemA_Light
{
    public void on()
    {
        Console.WriteLine("打开了灯....");
    }

    public void off()
    {
        Console.WriteLine("关闭了灯....");
    }
}

//电视类
public class SubSystemB_Television
{
    public void on()
    {
        Console.WriteLine("打开了电视....");
    }

    public void off()
    {
        Console.WriteLine("关闭了电视....");
    }
}

//空调类
public class SubSystemC_Aircondition
{
    public void on()
    {
        Console.WriteLine("打开了电视....");
    }

    public void off()
    {
        Console.WriteLine("关闭了电视....");
    }
}

  到这里我们如果需要关闭/打开电器,需要创建对象,来调用各自的打开和关闭方法。此时不使用外观模式的情况下,小成爷爷需要对每个电器都进行操作,非常不方便。

客户端与三个子系统都发送了耦合,使得客户端程序依赖与子系统引用块内容

怎么办呢?聪明小成买来了智能开关,小成把买来的智能家具控制器(外观对象/统一接口)给他爷爷,他爷爷只需要一键就能打开/关闭 灯、电视机、空调,十分方便。

即用外观模式来为所有子系统设计一个统一的接口
客户端只需要调用外观类中的方法就可以了,简化了客户端的操作

门面(智能控制开关):

public class MyFacade
{
    SubSystemA_Light light;
    SubSystemB_Television television;
    SubSystemC_Aircondition aircondition;

    public MyFacade()
    {
        light = new SubSystemA_Light();
        television = new SubSystemB_Television();
        aircondition = new SubSystemC_Aircondition();
    }

    public void Open()
    {
        Console.WriteLine("\n打开...");

        light.on();
        television.on();
        aircondition.on();
        Console.WriteLine("可以看电视了");
    }

    public void Close()
    {
        Console.WriteLine("\n关闭...");

        light.off();
        television.off();
        aircondition.off();
        Console.WriteLine("可以睡觉了");
    }
}

测试:

MyFacade myFacade = new MyFacade();

myFacade.Open();
myFacade.Close();

测试结果:

打开...
打开了灯...
打开了电视.
打开了电视.
可以看电视了

关闭...
关闭了灯...
关闭了电视.
关闭了电视.
可以睡觉了

  通过上述例子我想你已经理解了外观模式的原理。

3、外观模式优缺点

下面来分析外观模式的优缺点:

  • 优点

    1. 降低了客户类与子系统类的耦合度,实现了子系统与客户之间的松耦合关系

      理解

      1. 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类
      2. 减少了与子系统的关联对象,实现了子系统与客户之间 的松耦合关系,松耦合使得子系统的组件变化不会影响到它的客户。
    2. 外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单。

      理解

      1. 引入外观角色之后,用户只需要与外观角色交互;
      2. 用户与子系统之间的复杂逻辑关系由外观角色来实现;
    3. 降低原有系统的复杂度和系统中的编译依赖性,并简化了系统在不同平台之间的移植过程

      理解

      1. 因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
  • 缺点

    1. 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”;
    2. 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。

4、外观模式适用场景

下面我们来看看外观模式的适用场景:

  1. 要为一个复杂的子系统对外提供一个简单的接口;
  2. 提供子系统的独立性;
  3. 客户程序与多个子系统之间存在很大的依赖性 ;
  4. 引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性;
  5. 在层次化结构中,可以使用外观模式定义系统中每一层的入口 ,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。

5、应用举例(unity)

子系统父类:

using UnityEngine;
using UnityEngine.UI;

public class UI
{
    protected Text mText;
    protected Text mBtnText;
    protected Image mImage;

    protected void InitUI(Transform transform)
    {
        Button button = transform.Find("Button").GetComponent<Button>();
        button.onClick.RemoveAllListeners();
        button.onClick.AddListener(OnClick);

        mText = transform.Find("Text").GetComponent<Text>();
        mBtnText = transform.Find("Button/Text").GetComponent<Text>();
        mImage = transform.Find("Image").GetComponent<Image>();
    }

    protected virtual void OnClick()
    {
    }
}

子系统类A

using UnityEngine;
using UnityEngine.UI;

public class UIMethodA : UI
{
    protected override void OnClick()
    {
        Debug.Log("方法组A");
    }

    public void MethodA(Transform transform)
    {
        //这里设计的不是很合理,但是不知道要怎么改,以后想到了在做更改
        InitUI(transform);
        mText.text = "方法A";
        mBtnText.text = "方法A按钮";
        mImage.color = Color.green;
    }
}

子系统类B:

using UnityEngine;

public class UIMethodB : UI {

    protected override void OnClick()
    {
        Debug.Log("方法组B");
    }

    public void MethodB(Transform transform)
    {
        //这里设计的不是很合理,但是不知道要怎么改,以后想到了在做更改
        InitUI(transform);
        mText.text = "方法B";
        mBtnText.text = "方法B按钮";
        mImage.color = Color.yellow;
    }
}

门面类:

using UnityEngine;

public class Facade 
{
    UIMethodA a;
    UIMethodB b;

    public Facade()
    {
        a = new UIMethodA();
        b = new UIMethodB();
    }

    public void MethodA(Transform transform)
    {
        a.MethodA(transform);
    }

    public void MethodB(Transform transform)
    {
        b.MethodB(transform);
    }
}

测试类:

using UnityEngine;

public class Test : MonoBehaviour {

    void OnGUI()
    {
        if (GUI.Button(new Rect(100, 100, 120, 50), "方法组A"))
        {
            Facade f = new Facade();
            f.MethodA(this.transform);
        }

        if (GUI.Button(new Rect(100, 200, 120, 50), "方法组B"))
        {
            Facade f = new Facade();
            f.MethodB(this.transform);

        }
    }
}

我们把test挂在Canvas上,点击按钮A/B再点击白色按钮,得到测试结果如图:
这里写图片描述

这里写图片描述

6、与适配器模式的区别

  • 二者区别如下:
    1. 外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。
    2. 这样的实现方式非常类似适配器模式,然而外观模式与适配器模式不同的是:适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。

7、总结

  到这里外观模式的介绍就结束了。外观模式,为子系统的一组接口提供一个统一的接口,该模式定义了一个高层接口,这一个高层接口使的子系统更加容易使用。并且外观模式可以解决层结构分离、降低系统耦合度和为新旧系统交互提供接口功能。

8、unity工程下载

  在文章的最后我们给出上述例子的工程下载链接,方便朋友们探讨交流!本次演示版本Unity5.6.3f1(64-bit),VS2015需要下载的朋友,请点击此处下载


The End
  好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!


喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!

相关阅读
C# 23种设计模式(unity演示)

猜你喜欢

转载自blog.csdn.net/lxt610/article/details/81283652