在软件工程中,设计模式是解决常见设计问题的经验总结,它为开发者提供了一种通用的、可复用的解决方案。外观模式(Facade
Pattern)作为结构型设计模式的一种,通过为子系统中的一组接口提供一个统一的高层接口,使得子系统更加容易使用。
目录
- 一、外观模式的起源与背景
- 二、外观模式的组件
- 2.1. Facade(外观类)
- 2.2. Subsystem Classes(子系统类)
- 2.3. Client(客户端)
- 三、外观模式的优势分析
- 四、外观模式实现方式
- 五、外观模式的应用场景举例
- 总结
[参见]:
Java设计模式:核心概述(一)
Java设计模式:单例模式之六种实现方式详解(二)
Java设计模式:工厂模式之简单工厂、工厂方法、抽象工厂(三)
Java设计模式:建造者模式之经典与流式的三种实现(四)
Java设计模式:适配器模式的三种形式(五)
Java设计模式:深入装饰器模式的三种写法(六)
Java设计模式:组合模式之透明与安全的两种实现(七)
Java设计模式:代理模式的静态和动态之分(八)
一、外观模式的起源与背景
在复杂的软件系统中,往往存在多个子系统或组件协同工作的情况。这些子系统可能由不同的团队开发,使用不同的技术栈,具有各自独特的接口和依赖关系。当客户端需要使用这些子系统时,可能需要了解并调用多个接口,处理复杂的依赖关系,这无疑增加了客户端的使用难度。
为了解决这个问题,外观模式应运而生。它通过一个外观类(Facade)来整合子系统中的接口,为客户端提供一个统一的、更简单的接口。客户端只需与外观类交互,而无需了解子系统的具体实现细节和复杂性。
二、外观模式的组件
2.1. Facade(外观类)
- 外观类是外观模式的核心组件,它提供了客户端所需的统一接口。
- 外观类内部持有对子系统对象的引用,负责将客户端的请求转发给相应的子系统对象处理。
- 外观类可以对子系统的接口进行封装和抽象,使得客户端无需了解子系统的具体实现细节。
- 外观类还可以对子系统的返回结果进行整合和处理,以更友好的方式呈现给客户端。
2.2. Subsystem Classes(子系统类)
- 子系统类是实现具体功能的类,它们可能具有复杂的接口和相互依赖关系。
- 在外观模式中,子系统类被隐藏起来,客户端不直接访问它们。而是通过外观类来间接访问子系统。
- 子系统类之间可能存在复杂的交互和协作关系,但这些细节对客户端来说是透明的。
2.3. Client(客户端)
- 客户端是使用外观类来访问子系统的类。
- 客户端只需了解外观类提供的接口,而无需了解子系统的具体实现细节和复杂性。
- 通过使用外观类,客户端可以更加简洁、方便地访问子系统,降低了客户端的使用难度和耦合度。
三、外观模式的优势分析
- 简化客户端使用:通过提供一个统一的接口,外观模式使得客户端可以更加简洁、方便地访问子系统。客户端无需了解子系统的复杂性和实现细节,只需调用外观类提供的方法即可完成操作。这降低了客户端的使用难度和学习成本。
- 降低耦合度:外观模式将客户端与子系统解耦,客户端只需与外观类交互,而无需直接依赖子系统。当子系统发生变化时,只需要修改外观类而无需修改客户端代码。这提高了系统的可维护性和可扩展性。
- 隐藏系统复杂性:外观类封装了子系统的实现细节和复杂性,为客户端提供了一个清晰的、一致的视图。这使得客户端可以专注于自己的业务逻辑,而无需关心子系统的具体实现和交互细节。这有助于保护子系统的内部结构和实现细节不被外部直接访问和修改。
- 提高系统灵活性:通过引入外观类作为中介层,我们可以更加灵活地组织和管理子系统。我们可以根据需要添加、删除或修改子系统的组件,而无需修改客户端代码。这提高了系统的灵活性和可配置性。
四、外观模式实现方式
我们模拟一个银行系统,其中包括账户管理、贷款服务和存款服务等多个子系统。然后使用外观模式来提供一个统一的接口给客户端使用。
首先,我们定义几个子系统类(Subsystem Classes),这些类模拟了银行系统中的不同组件。
// 账户管理子系统
class AccountManagement {
public void createAccount(String accountName) {
System.out.println("为 " + accountName + " 创建账户。");
}
public void closeAccount(String accountName) {
System.out.println("关闭 " + accountName + " 的账户。");
}
}
// 贷款服务子系统
class LoanService {
public void applyForLoan(String accountName, double amount) {
System.out.println(accountName + " 申请了 " + amount + " 的贷款。");
}
public void approveLoan(String accountName) {
System.out.println(accountName + " 的贷款已被批准。");
}
}
// 存款服务子系统
class DepositService {
public void deposit(String accountName, double amount) {
System.out.println(accountName + " 存款 " + amount + "。");
}
public void withdraw(String accountName, double amount) {
System.out.println(accountName + " 取款 " + amount + "。");
}
}
接下来,我们创建外观类(Facade),该类将封装这些子系统类,并为客户端提供一个统一的接口。
// 银行外观类
class BankFacade {
private AccountManagement accountManagement;
private LoanService loanService;
private DepositService depositService;
// 构造函数,初始化子系统对象
public BankFacade() {
accountManagement = new AccountManagement();
loanService = new LoanService();
depositService = new DepositService();
}
// 提供给客户端的简化方法 - 开户并存款
public void openAccountAndDeposit(String accountName, double amount) {
accountManagement.createAccount(accountName);
depositService.deposit(accountName, amount);
System.out.println("开户并存款操作完成。");
}
// 提供给客户端的简化方法 - 申请贷款并批准
public void applyAndApproveLoan(String accountName, double amount) {
loanService.applyForLoan(accountName, amount);
loanService.approveLoan(accountName);
System.out.println("申请并批准贷款操作完成。");
}
// 提供给客户端的简化方法 - 取款并关闭账户
public void withdrawAndCloseAccount(String accountName, double amount) {
depositService.withdraw(accountName, amount);
accountManagement.closeAccount(accountName);
System.out.println("取款并关闭账户操作完成。");
}
}
最后,我们创建客户端(Client)来使用外观类。
// 客户端
public class BankClient {
public static void main(String[] args) {
// 创建银行外观对象
BankFacade bankFacade = new BankFacade();
// 客户端通过银行外观对象调用操作
bankFacade.openAccountAndDeposit("张三", 10000);
bankFacade.applyAndApproveLoan("张三", 5000);
bankFacade.withdrawAndCloseAccount("张三", 2000);
}
}
客户端通过BankFacade
类的简化方法来完成一系列复杂的操作,如开户并存款、申请贷款并批准、取款并关闭账户等。客户端无需了解子系统的具体实现细节和它们之间的交互方式。
运行BankClient
类的main
方法,看到以下输出:
为 张三 创建账户。
张三 存款 10000.0。
开户并存款操作完成。
张三 申请了 5000.0 的贷款。
张三 的贷款已被批准。
申请并批准贷款操作完成。
张三 取款 2000.0。
关闭 张三 的账户。
取款并关闭账户操作完成。
通过银行外观类,客户端成功地完成了一系列复杂的银行业务操作,而无需直接与各个子系统打交道。这样的设计降低了客户端与子系统之间的耦合度,提高了系统的可维护性和可扩展性。
五、外观模式的应用场景举例
- 在大型软件系统中整合不同模块:在大型软件系统中,往往存在多个模块或组件协同工作的情况。这些模块可能由不同的团队开发,具有各自独特的接口和依赖关系。通过使用外观模式,我们可以将这些模块的接口整合成一个统一的接口,使得其他模块可以更加简洁、方便地访问它们。
- 隐藏复杂系统的实现细节:当一个系统的实现细节非常复杂时,我们可以使用外观模式来隐藏这些细节。通过提供一个简化的接口给客户端使用,我们可以保护系统的内部结构和实现细节不被外部直接访问和修改。这有助于确保系统的稳定性和安全性。
- 在分布式系统中整合服务:在分布式系统中,往往存在多个服务协同工作的情况。这些服务可能部署在不同的服务器上,具有各自独特的接口和通信协议。通过使用外观模式,我们可以将这些服务的接口整合成一个统一的接口,使得客户端可以更加简洁、方便地访问它们。这降低了客户端与分布式系统之间的通信复杂性和耦合度。
总结
外观模式是一种非常实用的设计模式,它通过引入一个统一的接口来隐藏系统的复杂性,使得客户端可以更方便地使用子系统。在实际应用中,我们应该根据具体的需求和场景来选择合适的设计模式,以提高软件系统的可维护性、可扩展性和可重用性。外观模式为我们提供了一个有效的手段来简化客户端的使用和降低系统的复杂性。