1 介绍
1.1 引入
如果客户端要跟许多子系统打交道,那么客户端需要了解各个子系统的接口,比较麻烦。如果有一个统一的“中介”,让客户端只跟中介打交道,中介再去跟各个子系统打交道,对客户端来说就比较简单。
1.1 外观模式的定义与特点
外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
日常编码工作中,我们都在有意无意的大量使用外观模式。只要是高层模块需要调度多个子系统(2个以上的类对象),我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。
外观(Facade)模式包含以下主要角色:
- 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
- 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
好处:
- 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
- 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
缺点:
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”
2 案例
简单的实现引入中提到的场景
我们以注册公司为例,假设注册公司需要:
- 向工商局申请公司营业执照,拿到备案号
- 拿到工商局的备案号,去银行开设账户;
2.1 代码实现
- 定义两个模型
package study.wyy.design.facade.model;
import lombok.Data;
/**
* @author wyaoyao
* @description
* @date 2021/1/6 16:45
*/
@Data
public class Company {
/****
* 公司名称
*/
private String name;
/***
* 公司法人
*/
private String corporateName;
/****
* 公司地址
*/
private String address;
/****
* 备案号
*/
private String icp;
}
package study.wyy.design.facade.model;
import lombok.Data;
/**
* @author wyaoyao
* @description
* @date 2021/1/6 16:56
*/
@Data
public class BankAccount {
/****
* 账户号
*/
private String accountId;
/***
* 账户名
*/
private String accountName;
}
- 定义两个服务
- 公司注册服务
package study.wyy.design.facade.service;
import study.wyy.design.facade.model.Company;
import java.util.UUID;
/**
* @author wyaoyao
* @description 工商注册服务
* @date 2021/1/6 16:50
*/
public class AdminOfIndustryService {
/***
* 模拟公司注册,返回公司的备案号
* @param company
* @return
*/
public String registerCompany(Company company){
System.out.println("工商局公司备案完成");
return UUID.randomUUID().toString();
}
}
- 银行开户服务
package study.wyy.design.facade.service;
import study.wyy.design.facade.model.BankAccount;
import study.wyy.design.facade.model.Company;
import java.util.UUID;
/**
* @author wyaoyao
* @description
* @date 2021/1/6 16:55
*/
public class BankService {
/******
* 模拟银行开户
* * @param company
* @return
*/
public BankAccount openAccount(Company company){
BankAccount bankAccount = new BankAccount();
bankAccount.setAccountId(UUID.fromString(company.getIcp()).toString());
bankAccount.setAccountName(company.getName());
System.out.println("银行开户完成");
return bankAccount;
}
}
- 客户端调用
public static void main(String[] args) {
// 1 注册公司
AdminOfIndustryService adminOfIndustryService = new AdminOfIndustryService();
Company company = new Company();
company.setAddress("中国北京");
company.setCorporateName("张三");
company.setName("张三网络科技公司");
String icp = adminOfIndustryService.registerCompany(company);
// 2 银行开户
BankService bankService = new BankService();
company.setIcp(icp);
BankAccount bankAccount = bankService.openAccount(company);
}
可见客户端使用起来就过于麻烦,这个时候就可以提供一个门面,将这两个过程封装起来:
package study.wyy.design.facade.facade;
import study.wyy.design.facade.model.BankAccount;
import study.wyy.design.facade.model.Company;
import study.wyy.design.facade.service.AdminOfIndustryService;
import study.wyy.design.facade.service.BankService;
/**
* @author wyaoyao
* @description
* @date 2021/1/6 17:14
*/
public class CompanyFacade {
private final AdminOfIndustryService adminOfIndustryService;
private final BankService bankService;
public CompanyFacade() {
this.adminOfIndustryService = new AdminOfIndustryService();
this.bankService = new BankService();
}
public void register(Company company){
// 1 注册公司
String icp = adminOfIndustryService.registerCompany(company);
// 2 银行开户
company.setIcp(icp);
BankAccount bankAccount = bankService.openAccount(company);
}
}
此时客户端使用就变得简单了:
public static void main(String[] args) {
CompanyFacade companyFacade = new CompanyFacade();
Company company = new Company();
company.setAddress("中国北京");
company.setCorporateName("张三");
company.setName("张三网络科技公司");
companyFacade.register(company);
}