1、引言
在软件开发过程中,客户端程序经常会与复杂系统的内部子系统进行耦合,从而导致客户端程序随着子系统的变化而变化,然而为了将复杂系统的内部子系统与客户端之间的依赖解耦,从而就有了外观模式,也称作 ”门面“模式。下面就具体介绍下外观模式。
2、外观模式详细介绍
2.1、定义
- 外观模式(Facade Pattern):
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。
外观定义了一个高层接口,让子系统更容易使用。使用外观模式时,我们创建了一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以直接通过外观类来调用内部子系统中方法,从而外观模式让客户和子系统之间避免了紧耦合。
如图:
2.2、作用
该模式的作用如下:
- 实现客户类与子系统类的松耦合
- 降低原有系统的复杂度
- 提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。
- 引入外观角色之后,用户只需要与外观角色交互;
- 用户与子系统之间的复杂逻辑关系由外观角色来实现;
2.3、解决的问题
该模式解决了一下问题:
- 避免了系统与系统之间的高耦合度
- 使得复杂的子系统用法变得简单
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、外观模式优缺点
下面来分析外观模式的优缺点:
优点
降低了客户类与子系统类的耦合度,实现了子系统与客户之间的松耦合关系
理解
- 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类
- 减少了与子系统的关联对象,实现了子系统与客户之间 的松耦合关系,松耦合使得子系统的组件变化不会影响到它的客户。
外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单。
理解
- 引入外观角色之后,用户只需要与外观角色交互;
- 用户与子系统之间的复杂逻辑关系由外观角色来实现;
降低原有系统的复杂度和系统中的编译依赖性,并简化了系统在不同平台之间的移植过程
理解
- 因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
缺点
- 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”;
- 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
4、外观模式适用场景
下面我们来看看外观模式的适用场景:
- 要为一个复杂的子系统对外提供一个简单的接口;
- 提供子系统的独立性;
- 客户程序与多个子系统之间存在很大的依赖性 ;
- 引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性;
- 在层次化结构中,可以使用外观模式定义系统中每一层的入口 ,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
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、与适配器模式的区别
- 二者区别如下:
- 外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。
- 这样的实现方式非常类似适配器模式,然而外观模式与适配器模式不同的是:适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。
7、总结
到这里外观模式的介绍就结束了。外观模式,为子系统的一组接口提供一个统一的接口,该模式定义了一个高层接口,这一个高层接口使的子系统更加容易使用。并且外观模式可以解决层结构分离、降低系统耦合度和为新旧系统交互提供接口功能。
8、unity工程下载
在文章的最后我们给出上述例子的工程下载链接,方便朋友们探讨交流!本次演示版本Unity5.6.3f1(64-bit),VS2015需要下载的朋友,请点击此处下载。
The End
好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!
喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!
相关阅读
C# 23种设计模式(unity演示)