本文中涉及到的完整代码存放于以下 GitHub 仓库中 LearningCode
1. 理论部分
外观模式(Facade Pattern):为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式也称门面模式。
1.1 结构与实现
外观模式包含以下 2 个角色:
- Facade(外观角色):
- 职责:在客户端可以调用它的方法,在外观角色中可以知道相关(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统,传递给相应的子系统对象处理。
- 实现:通常声明为接口或抽象类,也可以为具体类。
- SubSystem(子系统角色):
- 职责:在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
- 实现:通常声明为具体类。
外观模式的 UML 类图如下所示:
在标准外观模式中,如果需要增加、删除或更换与外观类交互的子系统类,必须修改外观类或客户端的源代码,这将违背开闭原则,因此可以通过引入抽象外观类对系统进行改进,这在一定程度上可以解决该问题。其 UML 类图如下所示:
1.2 优缺点与适用场景
外观模式具有以下优点:
- 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。
- 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
- 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
外观模式存在以下缺点:
- 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性。
- 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。
外观模式适用于以下场景:
- 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。
- 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。
- 在层次化结构中可以使用外观模式定义系统中每一层的人口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
2. 实现部分
以 Java 代码为例,演示外观模式的实现。
案例介绍:
2.1 外观模式的实现
读取源文件的代码
public class FileReader {
public String read(String filename) {
System.out.println("读取文件,获取明文");
return "明文";
}
}
加密的代码
public class CipherMachine {
public String encrypt(String plainText) {
System.out.println("使用加密方式一对数据进行加密,将明文转换为密文。");
return "密文";
}
}
保存加密字符串到文件中的代码
public class FileWriter {
public void write(String encryptStr, String filename) {
System.out.println("保持密文,写入文件");
}
}
编写外观类——EncryptFacade
public class EncryptFacade {
private final FileReader reader;
private final CipherMachine cipher;
private final FileWriter writer;
public EncryptFacade() {
reader = new FileReader();
cipher = new CipherMachine();
writer = new FileWriter();
}
public void fileEncrypt(String filenameSrc, String filenameDes) {
String plainStr = reader.read(filenameSrc);
String encryptStr = cipher.encrypt(plainStr);
writer.write(encryptStr, filenameDes);
}
}
客户端调用
public class Main {
public static void main(String[] args) {
EncryptFacade ef = new EncryptFacade();
ef.fileEncrypt("src", "des");
}
}
完整的 UML 类图如下所示:
2.2 引入抽象外观类
在上述代码实现中,若要更换加密算法需要修改外观类,不符合开闭原则,因此引入抽象外观类。
定义抽象外观类——AbstractEncryptFacade
public interface AbstractEncryptFacade {
void fileEncrypt(String filenameSrc, String filenameDes);
}
修改外观类EncryptFacade
public class EncryptFacade implements AbstractEncryptFacade{
private final FileReader reader;
private final CipherMachine cipher;
private final FileWriter writer;
public EncryptFacade() {
reader = new FileReader();
cipher = new CipherMachine();
writer = new FileWriter();
}
@Override
public void fileEncrypt(String filenameSrc, String filenameDes) {
String plainStr = reader.read(filenameSrc);
String encryptStr = cipher.encrypt(plainStr);
writer.write(encryptStr, filenameDes);
}
}
定义新的加密算法
public class NewCipherMachine {
public String encrypt(String plainText) {
System.out.println("使用加密方式二对数据进行加密,将明文转换为密文。");
return "密文";
}
}
定义新的外观类——NewEncryptFacade
public class NewEncryptFacade implements AbstractEncryptFacade{
private final FileReader reader;
private final NewCipherMachine cipher;
private final FileWriter writer;
public NewEncryptFacade() {
reader = new FileReader();
cipher = new NewCipherMachine();
writer = new FileWriter();
}
@Override
public void fileEncrypt(String filenameSrc, String filenameDes) {
String plainStr = reader.read(filenameSrc);
String encryptStr = cipher.encrypt(plainStr);
writer.write(encryptStr, filenameDes);
}
}
修改客户端调用
public class Main {
public static void main(String[] args) {
AbstractEncryptFacade ef = new EncryptFacade();
ef.fileEncrypt("src", "des");
}
}
完整的 UML 类图如下所示:
3. 参考资料
学习视频:
- 设计模式快速入门 —— 图灵星球TuringPlanet —— 外观模式
- Java设计模式详解 —— 黑马程序员 —— 外观模式(P78 ~ P81)
- Java设计模式 —— 尚硅谷 —— 外观模式(P81 ~ P85)
学习读物:
- 《设计模式:可复用面向对象软件的基础》—— Erich Gamma 著 —— 李英军 译 —— 第 4.5 节(P139)
- 《Java 设计模式》 —— 刘伟 著 —— 第 13 章(P174)
- 《设计模式之美》—— 王争 著 —— 第 7.5 节(P231)
- 《设计模式之禅》 —— 第 2 版 —— 秦小波 著 —— 第 23 章(P278)
- 《图解设计模式》—— 结城浩 著 —— 杨文轩 译 —— 第 15 章(P171)
电子文献: