门面模式,最简单的结构性设计模式,将多个不同的子系统逻辑封装起来,对外提供统一的调用接口。门面模式又叫做外观模式,可能是我们接触最多的模式,在开发中,可能不经意间就用到了门面模式。
1. 概述
门面模式 的官方定义是:客户端和多个子系统的通信会通过一个统一的外观角色进行,这个角色为子系统中的一组接口提供了一个一致的入口,它是一种结构性的设计模式。
根据单一职责的描述,在软件中会将一个系统划分成为若干个子系统,这有利于降低整个系统的复杂性。但多个系统之间存在同步或者异步的交互关系,拓扑结构极为复杂,为了解决这种情况,一个常见的设计方式便是引入一个外观的角色,使得外部客户端与子系统之间的相互依赖关系达到最小。
门面模式是迪米特法则的最佳体现,引入一个新的外观角色可以降低系统的复杂度,同时也能降低客户类和子系统的耦合度。举个开发中常见的门面模式,controller
层对多个数据表进行保存,会调用到多个表对应的service
层,但是我们一般会在service
层封装一个专用外观类来实现对多个表保存的逻辑,controller
层只需要与这个外观类交互就可以。
在门面模式中,通常存在两个角色:
- 外观角色:与客户端交互的类,封装了子系统的交互逻辑,对外提供统一的访问入口。
- 子系统:子系统是一个广义的概念,可以是一个类、一个功能模块或者是一个完整的子系统。子系统能够实现具体的功能。
2. 代码实现
我们来看一个代码实现,需求是要求实现读取本地文件,加密后从新存储的功能。这个需求可以拆分成3个模块来实现,1.文件读取 → 2.内容加密 → 3.写入磁盘。如下是代码实现:
- 1. 子系统
// 1.文件阅读
public class FileReader {
// 文件读取
public String read(String path) {
StringBuffer stringBuffer = new StringBuffer();
// 读取本地文件
try (FileInputStream fileInputStream = new FileInputStream(path)) {
// 获取流信息并且转为字符串返回
int data;
while ((data = fileInputStream.read()) != -1) {
stringBuffer.append((char)data);
}
} catch (Exception e) {
e.printStackTrace();
}
return stringBuffer.toString();
}
}
// 2.字符串加密
public class EncryptOperate {
// AES算法加密
public String encrypt(String content) {
//随机生成密钥
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
//构建
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
//加密为16进制表示
String encryptHex = aes.encryptHex(content);
return encryptHex;
}
}
// 3.保存文件到本地
public class FileWriter {
// 写入文件
public void write(String encrypt, String path) {
// 写入本地
try (FileOutputStream outputStream = new FileOutputStream(path)) {
outputStream.write(encrypt.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 2. 门面类
// 门面类
public class Facade {
private FileReader fileReader;
private EncryptOperate encryptOperate;
private FileWriter fileWriter;
Facade() {
this.fileReader = new FileReader();
this.encryptOperate = new EncryptOperate();
this.fileWriter = new FileWriter();
}
// 文件操作
public void operate(String path) {
String content = fileReader.read(path);
String encrypt = encryptOperate.encrypt(content);
fileWriter.write(encrypt, "/user/xxx/file/设计模式.txt");
}
}
- 3. 客户端
// 客户端
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operate("/user/xxx/file/设计模式.txt");
}
}
3. UML类图
根据上述代码实现,我们来画一下这个Demo的类图。
4. 抽象门面角色的引入
在实际开发中,如果遇到多种门面类的实现场景,可以增加一个抽象的门面角色,让客户端与抽象的门面类交互。这样在遇到新的门面实现类时,不用修改原来的代码,增加一个实现就可以,也符合开闭原则。
5. 总结
门面模式优点如下:
- 门面模式对客户端屏蔽了子系统的复杂拓扑关系,使得客户端的调用逻辑简单直接,理解和改动的成本降低。
- 子系统和客户端之间松耦合,当需要增加新的门面实现逻辑时,可以新增一整套子系统的逻辑实现,符合开闭原则。
门面模式的应用场景:
- slf4j门面模式