目录
- 外观模式概述
- 外观模式的结构与实现
- 外观模式的应用实例
- 抽象外观类
- 外观模式的优缺点与适用环境
外观模式概述
两种喝茶方式示意图:
分析:
- 一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现
- 引入一个新的外观类(Facade)来负责和多个业务类【子系统(Subsystem)】进行交互,而客户类只需与外观类交互
- 为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互
没有外观类:每个客户类需要和多个子系统之间进行复杂的交互,系统的耦合度将很大
引入外观类:客户类只需要直接与外观类交互,客户类与子系统之间原有的复杂引用关系由外观类来实现,从而降低了系统的耦合度
一个子系统的外部与其内部的通信通过一个统一的外观类进行,外观类将客户类与子系统的内部复杂性分隔开,使得客户类只需要与外观角色打交道,而不需要与子系统内部的很多对象打交道
外观模式的定义:
- 外观模式:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- Facade Pattern: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
- 对象结构型模式
注:
- 又称为门面模式
- 是迪米特法则的一种具体实现
- 通过引入一个新的外观角色来降低原有系统的复杂度,同时降低客户类与子系统的耦合度
- 所指的子系统是一个广义的概念,它可以是一个类、一个功能模块、系统的一个组成部分或者一个完整的系统
外观模式的结构与实现
外观模式的结构:
外观模式包含以下2个角色:
- Facade(外观角色)
在客户端可以调用这个角色的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任。在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相依的子系统对象处理。
- SubSystem(子系统角色)
在软件系统中可以同时有一个或多个子系统角色,每个子系统可以不是一个单独的类,而是一个类的集合,他实现子系统的功能。每个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求。子系统并不知道外观的存在,对于子系统而言,外观仅仅是一个客户端而且。
注:对于多个子系统角色而言,彼此之间相互独立,没有任何的逻辑关系。
外观模式的实现
经典的外观类代码:
public class Facade {
private SubSystemA obj1 = new SubSystemA();
private SubSystemB obj2 = new SubSystemB();
private SubSystemC obj3 = new SubSystemC();
public void method() {
obj1.methodA();
obj2.methodB();
obj3.methodC();
}
}
经典的子系统类代码:
public class SubSystemA {
public void methodA() {
}
}
public class SubSystemB {
public void methodB() {
}
}
public class SubSystemC {
public void methodC() {
}
}
经典的客户端类代码:
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.method();
}
}
外观模式的应用实例
某软件公司要开发一个可应用于多个软件的文件加密模块,该模块可以对文件中的数据进行加密并将加密之后的数据存储在一个新文件中,具体的流程包括3个部分,分别是读取源文件、加密、保存加密之后的文件,其中,读取文件和保存文件使用流来实现,加密操作通过求模运算实现。这3个操作相对独立,为了实现代码的独立重用,让设计更符合单一职责原则,这3个操作的业务代码封装在3个不同的类中。
现使用外观模式设计该文件加密模块。
实例类图:
实例代码:
- (1) FileReader:文件读取类,充当子系统类
- (2) CipherMachine:数据加密类,充当子系统类
- (3) FileWriter:文件保存类,充当子系统类
- (4) EncryptFacade:加密外观类,充当外观类
- (5) Program:客户端测试类
抽象外观类
动机:在标准的外观模式的结构图中,如果需要增加、删除或更换与外观类交互的子系统类,必须修改外观类或客户端的源代码,这将违背开闭原则,因此可以通过引入抽象外观类对系统进行改进,在一定程度上解决该问题
结构:
实现(C#):
namespace FacadeSample
{
abstract class AbstractEncryptFacade
{
public abstract void FileEncrypt(string fileNameSrc, string fileNameDes);
}
}
namespace FacadeSample
{
class NewEncryptFacade : AbstractEncryptFacade
{
private FileReader reader;
private NewCipherMachine cipher;
private FileWriter writer;
public NewEncryptFacade()
{
reader = new FileReader();
cipher = new NewCipherMachine();
writer = new FileWriter();
}
public override void FileEncrypt(string fileNameSrc, string fileNameDes)
{
string plainStr = reader.Read(fileNameSrc);
string encryptStr = cipher.Encrypt(plainStr);
writer.Write(encryptStr, fileNameDes);
}
}
}
using System;
using System.Configuration;
using System.Reflection;
namespace FacadeSample
{
class Program
{
static void Main(string[] args)
{
AbstractEncryptFacade ef; //针对抽象外观类编程
//读取配置文件
string facadeString = ConfigurationManager.AppSettings["facade"];
//反射生成对象
ef = (AbstractEncryptFacade)Assembly.Load("FacadeSample").CreateInstance (facadeString);
ef.FileEncrypt("src.txt", "des.txt");
Console.Read();
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="facade" value="FacadeSample.NewEncryptFacade"/>
</appSettings>
</configuration>
外观模式的优缺点与适用环境
模式优点:
- 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易
- 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可
- 一个子系统的修改对其他子系统没有任何影响,而且子系统的内部变化也不会影响到外观对象
模式缺点:
- 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性
- 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则
模式适用环境:
- 要为访问一系列复杂的子系统提供一个简单入口
- 客户端程序与多个子系统之间存在很大的依赖性
- 在层次化结构中,可以使用外观模式的定义系统中每一层的入口,层与层之间不直接产生联系,而是通过外观类建立联系,降低层之间的耦合度