外观模式
大约 9 分钟
外观模式
什么是外观模式
外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式的核心思想是:
- 为复杂的子系统提供一个简单的接口
- 隐藏子系统的复杂性
- 提供一个统一的访问点
为什么需要外观模式
在实际开发中,我们经常会遇到复杂的子系统,这些子系统可能包含很多类和接口,直接使用这些子系统会非常复杂。外观模式通过提供一个简化的接口来解决这个问题:
- 简化复杂系统:将复杂的子系统封装起来,提供简单的接口
- 松耦合:客户端与子系统解耦,降低依赖性
- 提高可用性:使子系统更容易使用
外观模式的结构
外观模式包含以下几个角色:
- 外观(Facade):知道哪些子系统类负责处理请求,将客户的请求代理给适当的子系统对象
- 子系统类(SubSystem):实现子系统的功能,处理由Facade对象指派的任务,注意子系统类不包含Facade类的引用
- 客户端(Client):通过Facade接口调用子系统的功能
外观模式的实现
基本实现
// 子系统A
class SubSystemA {
public void operationA() {
System.out.println("子系统A的操作");
}
}
// 子系统B
class SubSystemB {
public void operationB() {
System.out.println("子系统B的操作");
}
}
// 子系统C
class SubSystemC {
public void operationC() {
System.out.println("子系统C的操作");
}
}
// 外观类
class Facade {
private SubSystemA subSystemA;
private SubSystemB subSystemB;
private SubSystemC subSystemC;
public Facade() {
this.subSystemA = new SubSystemA();
this.subSystemB = new SubSystemB();
this.subSystemC = new SubSystemC();
}
// 简化的方法
public void operation1() {
System.out.println("外观方法1:");
subSystemA.operationA();
subSystemB.operationB();
}
public void operation2() {
System.out.println("外观方法2:");
subSystemB.operationB();
subSystemC.operationC();
}
public void operation3() {
System.out.println("外观方法3:");
subSystemA.operationA();
subSystemC.operationC();
}
}
// 使用示例
public class FacadeDemo {
public static void main(String[] args) {
Facade facade = new Facade();
// 使用外观提供的简化接口
facade.operation1();
System.out.println();
facade.operation2();
System.out.println();
facade.operation3();
}
}
实际应用示例
// 计算机启动示例
// CPU子系统
class CPU {
public void freeze() {
System.out.println("CPU冻结");
}
public void jump(long position) {
System.out.println("CPU跳转到位置: " + position);
}
public void execute() {
System.out.println("CPU执行指令");
}
}
// 内存子系统
class Memory {
public void load(long position, byte[] data) {
System.out.println("内存加载数据到位置: " + position);
}
}
// 硬盘子系统
class HardDrive {
public byte[] read(long lba, int size) {
System.out.println("硬盘读取数据,起始位置: " + lba + ",大小: " + size);
return new byte[size];
}
}
// 计算机外观类
class Computer {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public Computer() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
// 启动计算机的简化接口
public void start() {
System.out.println("=== 启动计算机 ===");
cpu.freeze();
memory.load(0, hardDrive.read(0, 1024));
cpu.jump(0);
cpu.execute();
System.out.println("=== 计算机启动完成 ===");
}
// 关闭计算机的简化接口
public void shutdown() {
System.out.println("=== 关闭计算机 ===");
// 简化的关闭过程
System.out.println("保存数据");
System.out.println("关闭所有进程");
System.out.println("断电");
System.out.println("=== 计算机关闭完成 ===");
}
}
// 使用示例
public class ComputerFacadeDemo {
public static void main(String[] args) {
Computer computer = new Computer();
// 用户只需要调用简单的接口
computer.start();
System.out.println();
computer.shutdown();
}
}
外观模式的应用场景
- 为复杂的模块或子系统提供外界访问的接口:当子系统因为不断演化而变得越来越复杂时,使用外观模式可以提供一个简单的接口,从而简化对子系统的访问
- 客户程序与抽象类的实现部分之间存在着很大的依赖性:引入外观模式将这个子系统与客户以及其他子系统分离,可以提高子系统的独立性和可移植性
- 当需要构建一个层次结构的子系统时:使用外观模式定义子系统中每层的入口点,如果子系统之间是相互依赖的,可以使用外观模式简化它们之间的依赖关系
- Spring框架:Spring的ApplicationContext就是外观模式的体现
外观模式的优缺点
优点
- 减少系统相互依赖:客户端不需要直接访问子系统,减少了客户端与子系统的依赖关系
- 提高灵活性:不管子系统内部如何变化,只要外观类不发生变化,系统就可以正常运行
- 提高安全性:想让客户端访问子系统的哪些业务就开放哪些业务,屏蔽不需要的业务
- 简化客户端调用:客户端不需要了解子系统的具体实现,只需要调用外观类提供的方法即可
缺点
- 不符合开闭原则:一旦在外观类中增加了新的方法,就需要修改外观类,违背了开闭原则
- 增加新的子系统可能需要修改外观类:如果增加新的子系统,可能需要修改外观类,这违背了开闭原则
外观模式与其他模式的比较
与适配器模式的区别
- 外观模式:定义新的接口,提供一个统一的高层接口
- 适配器模式:复用原有的接口,将一个接口转换成另一个接口
与装饰器模式的区别
- 外观模式:简化接口,隐藏复杂性
- 装饰器模式:增强接口,添加新功能
与中介者模式的区别
- 外观模式:对子系统进行抽象,提供一个统一的接口
- 中介者模式:封装一系列对象的交互,使对象不需要显式地相互引用
外观模式的变体
1. 抽象外观模式
// 抽象外观类
abstract class AbstractFacade {
public abstract void operation1();
public abstract void operation2();
}
// 具体外观类
class ConcreteFacade extends AbstractFacade {
private SubSystemA subSystemA = new SubSystemA();
private SubSystemB subSystemB = new SubSystemB();
@Override
public void operation1() {
subSystemA.operationA();
subSystemB.operationB();
}
@Override
public void operation2() {
subSystemB.operationB();
}
}
2. 门面工厂模式
// 门面工厂
class FacadeFactory {
public static Facade createFacade() {
return new Facade();
}
// 根据配置创建不同的外观
public static Facade createFacade(String type) {
switch (type) {
case "simple":
return new SimpleFacade();
case "advanced":
return new AdvancedFacade();
default:
return new Facade();
}
}
}
// 简单外观
class SimpleFacade extends Facade {
@Override
public void operation1() {
System.out.println("简单外观的操作1");
}
}
// 高级外观
class AdvancedFacade extends Facade {
@Override
public void operation1() {
System.out.println("高级外观的操作1");
super.operation1(); // 调用父类方法
}
}
实际项目中的应用
// 银行服务外观示例
// 银行账户子系统
class AccountService {
public boolean isValidAccount(String accountNumber) {
System.out.println("验证账户: " + accountNumber);
return true; // 简化实现
}
public double getBalance(String accountNumber) {
System.out.println("获取账户余额: " + accountNumber);
return 1000.0; // 简化实现
}
}
// 转账服务子系统
class TransferService {
public boolean transfer(String fromAccount, String toAccount, double amount) {
System.out.println("转账操作:");
System.out.println(" 从账户: " + fromAccount);
System.out.println(" 到账户: " + toAccount);
System.out.println(" 金额: $" + amount);
return true; // 简化实现
}
}
// 安全服务子系统
class SecurityService {
public boolean authenticate(String username, String password) {
System.out.println("用户认证: " + username);
return true; // 简化实现
}
public boolean authorize(String username, String operation) {
System.out.println("用户授权: " + username + " 执行 " + operation);
return true; // 简化实现
}
}
// 通知服务子系统
class NotificationService {
public void sendNotification(String message, String recipient) {
System.out.println("发送通知给 " + recipient + ": " + message);
}
}
// 银行服务外观类
class BankServiceFacade {
private AccountService accountService;
private TransferService transferService;
private SecurityService securityService;
private NotificationService notificationService;
public BankServiceFacade() {
this.accountService = new AccountService();
this.transferService = new TransferService();
this.securityService = new SecurityService();
this.notificationService = new NotificationService();
}
// 简化的转账接口
public boolean transferMoney(String username, String password,
String fromAccount, String toAccount, double amount) {
System.out.println("=== 开始转账操作 ===");
// 1. 用户认证
if (!securityService.authenticate(username, password)) {
System.out.println("认证失败");
return false;
}
// 2. 验证账户
if (!accountService.isValidAccount(fromAccount)) {
System.out.println("源账户无效");
return false;
}
if (!accountService.isValidAccount(toAccount)) {
System.out.println("目标账户无效");
return false;
}
// 3. 授权检查
if (!securityService.authorize(username, "transfer")) {
System.out.println("授权失败");
return false;
}
// 4. 检查余额
double balance = accountService.getBalance(fromAccount);
if (balance < amount) {
System.out.println("余额不足");
return false;
}
// 5. 执行转账
boolean success = transferService.transfer(fromAccount, toAccount, amount);
// 6. 发送通知
if (success) {
notificationService.sendNotification(
"转账成功,金额: $" + amount, username);
}
System.out.println("=== 转账操作完成 ===");
return success;
}
// 简化的查询余额接口
public double checkBalance(String username, String password, String accountNumber) {
System.out.println("=== 查询余额 ===");
// 认证
if (!securityService.authenticate(username, password)) {
System.out.println("认证失败");
return -1;
}
// 验证账户
if (!accountService.isValidAccount(accountNumber)) {
System.out.println("账户无效");
return -1;
}
// 授权
if (!securityService.authorize(username, "check_balance")) {
System.out.println("授权失败");
return -1;
}
// 获取余额
double balance = accountService.getBalance(accountNumber);
System.out.println("账户余额: $" + balance);
System.out.println("=== 查询完成 ===");
return balance;
}
}
// 使用示例
public class BankFacadeDemo {
public static void main(String[] args) {
BankServiceFacade bankService = new BankServiceFacade();
// 转账操作
System.out.println("1. 执行转账操作:");
boolean transferResult = bankService.transferMoney(
"张三", "password123", "ACC001", "ACC002", 500.0);
System.out.println("转账结果: " + (transferResult ? "成功" : "失败"));
System.out.println("\n------------------------\n");
// 查询余额
System.out.println("2. 查询账户余额:");
double balance = bankService.checkBalance("张三", "password123", "ACC001");
System.out.println("最终余额: $" + balance);
}
}
// Spring框架中的外观模式示例
// 模拟Spring的ApplicationContext
interface ApplicationContext {
Object getBean(String name);
<T> T getBean(String name, Class<T> requiredType);
<T> T getBean(Class<T> requiredType);
boolean containsBean(String name);
}
// 简化的Spring容器实现
class SimpleApplicationContext implements ApplicationContext {
private Map<String, Object> beans = new HashMap<>();
public SimpleApplicationContext() {
// 初始化一些Bean
beans.put("userService", new UserService());
beans.put("orderService", new OrderService());
beans.put("emailService", new EmailService());
}
@Override
public Object getBean(String name) {
return beans.get(name);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) {
Object bean = beans.get(name);
if (bean != null && requiredType.isInstance(bean)) {
return requiredType.cast(bean);
}
return null;
}
@Override
public <T> T getBean(Class<T> requiredType) {
for (Object bean : beans.values()) {
if (requiredType.isInstance(bean)) {
return requiredType.cast(bean);
}
}
return null;
}
@Override
public boolean containsBean(String name) {
return beans.containsKey(name);
}
}
// 服务类
class UserService {
public void createUser(String username) {
System.out.println("创建用户: " + username);
}
}
class OrderService {
public void createOrder(String orderId) {
System.out.println("创建订单: " + orderId);
}
}
class EmailService {
public void sendEmail(String to, String message) {
System.out.println("发送邮件到 " + to + ": " + message);
}
}
// 使用示例
public class SpringFacadeDemo {
public static void main(String[] args) {
// 客户端只需要与ApplicationContext交互
ApplicationContext context = new SimpleApplicationContext();
// 获取并使用服务,而不需要知道具体的实现细节
UserService userService = context.getBean("userService", UserService.class);
OrderService orderService = context.getBean("orderService", OrderService.class);
EmailService emailService = context.getBean("emailService", EmailService.class);
userService.createUser("张三");
orderService.createOrder("ORD001");
emailService.sendEmail("zhangsan@example.com", "欢迎注册");
}
}
总结
外观模式是一种非常实用的结构型设计模式,它通过为复杂的子系统提供一个简化的高层接口,使得子系统更容易使用。在实际开发中,当我们面对复杂的系统或需要为客户提供简化的访问接口时,外观模式是一个很好的选择。
使用外观模式的关键点:
- 识别出复杂的子系统
- 创建外观类来封装子系统的复杂性
- 提供简化的接口给客户端使用
- 客户端通过外观类与子系统交互,而不需要了解子系统的具体实现
外观模式的优点是可以简化客户端代码,降低系统间的耦合度,但也需要注意可能会违背开闭原则。在现代Java开发中,外观模式广泛应用于框架设计、API封装、系统集成等场景,是简化复杂系统访问的重要手段。