一、引言
上一篇博文我们介绍了面向对象的程序设计思想中的依赖倒转原则和迪米特法则,本文介绍的外观模式正式这两个原则的最佳体现。现实中存在的许多高级系统(事件)都是由许多复杂的小系统(事件)组成的。比如理财就可以看作是一个高级系统,用户资金的投资渠道包括股票、银行、保险和不动产等,那么投资股票、银行、保险和不同产就是构成理财的小系统。一般的用户很难对股票、银行等所有投资渠道进行深入的了解,由用户自己进行理财风险较大,因此很多人选择专业的理财机构进行托管理财。这样,用户不需要了解股票、银行等金融机构的行情,只需购买相应的理财产品即可,大大浇地了风险和操作难度,这就是外观模式的一种体现。
二、模式解析
2.1 模式分析
外观模式为子系统中的一组接口提供了一个一致的界面,此模式定义了一个高级接口,在高级接口中对子系统中的各个接口进行了组合,构造出一系列的方法,用户使用时可以直接通过调用高级接口的这些方法实现对子接口的调用,而不必自己直接调用子接口中的方法。
从图-1给出的外观模式UML图 可以看出,外观模式将一个高级系统定义为了一个外观类Facade,而构成高级系统的各个子系统被定义为了相应的子系统类Subsystem n,Facade类中维护了指向这些子系统类的成员变量,并根据用户的不同需求对这些子系统中的方法进行了组合构成了不同的解决方案MethodN(),这样用户就可以根据自己的需求直接调用Facade类中的相应Method方法,而不需要直接操作子系统类,从而简化了系统的应用难度。
图-1 外观模式UML图
2.2 分析与实现
我们依然使用引言中理财的例子说明外观模式的具体实现。显然,在本例中,理财机构是高级系统,而股票、银行、保险和不同产属于构成理财系统的子系统。因此我们首先定义一个理财机构类,它维护了四个分别指向股票、银行、保险和不同产的成员变量,并根据不同用户的需求指定了不同的理财方案。
/**
* 相当与外观类,该类中组合了所有子系统接口,并提够了一些封装好的子系统方法集合,用户可以直接调用
* 这些组合而不必直接调用子系统对象。
*/
class FinancialAgency {
// 维护着指向不同金融产品的引用
private Stock stock = new Stock();
private Bank bank = new Bank();
private Insurance insurance = new Insurance();
private RealEstate realEstate = new RealEstate();
// 不同的理财方案
public void financialSolution1() {
System.out.print("20%: ");
stock.solution1();
System.out.print("50%: ");
bank.solution1();
System.out.print("20%: ");
insurance.solution2();
System.out.print("10%: ");
realEstate.solution1();
}// financialSolutions1
public void financialSolution2() {
System.out.print("30%: ");
stock.solution2();
System.out.print("30%: ");
bank.solution2();
System.out.print("20%: ");
insurance.solution2();
System.out.print("20%: ");
realEstate.solution2();
}// financialSolutions2
public void financialSolution3() {
System.out.print("10%: ");
stock.solution1();
System.out.print("70%: ");
bank.solution1();
System.out.print("10%: ");
insurance.solution1();
System.out.print("10%: ");
realEstate.solution2();
}// financialSolutions3
}/*FinancialAgency*/
接下来是各个金融产品的定义,这里仅为说明外观模式的特点,因此定义地较为简单:
/**
* 子系统:股票类
*/
class Stock {
public void solution1() {
System.out.println("Buy stock of Huawei!");
}// solution1
public void solution2() {
System.out.println("Buy stock of Baidu!");
}// solution2
}/*Stock*/
/**
* 子系统:银行类
*/
class Bank {
public void solution1() {
System.out.println("Save money in bank!");
}// solution1
public void solution2() {
System.out.println("Buy national debt!");
}// solution2
}/*Bank*/
/**
* 子系统:保险类
*/
class Insurance {
public void solution1() {
System.out.println("Buy medical insurance!");
}// solution1
public void solution2() {
System.out.println("Buy personal accident insurance!");
}// solution2
}/*Insurance*/
/**
* 子系统:不动产类
*/
class RealEstate {
public void solution1() {
System.out.println("Buy house!");
}// solution1
public void solution2() {
System.out.println("Buy land!");
}// solution2
}/*RealEstate*/
最后是客户端代码,可以看出,在客户端中,用户只需要了解理财机构类即可实现合理理财的目的,而不需要对具体的金融产品类进行了解,这就是外观模式命名的由来,用户看到的只有系统的外观,但看不到系统的五脏六腑(内部组成)。
public class FacadeDemo {
/**
* 在客户端用户只需了解面向用户的FinancialAgency类,而不需要对所有理财子系统类进行了解
* @param args
*/
public static void main(String[] args) {
FinancialAgency agency = new FinancialAgency();
System.out.println("用户选择理财方案1:");
agency.financialMethod1();
System.out.println("用户选择理财方案2:");
agency.financialMethod2();
System.out.println("用户选择理财方案3:");
agency.financialMethod3();
}// main
}/*FacadeDemo*/
程序运行结果:
三、总结
外观模式将用户与繁杂众多的子系统隔离开来,降低了类之间的耦合,满足了依赖倒转原则和迪米特法则,并对常用的功能进行了组合,大大简化了系统使用的复杂度。它有三种常见的应用场景:
第一:在产品设计阶段,应该有意识的将 不同的两个层分离(三层架构:数据访问层、业务逻辑层和表示层),这样就需要在层与层之间建立外观Facade,这样一个层中的对象不需要对另外一个层内的对象直接产生联系,而是通过两个层之间的Facade建立连接。
第二:在开发阶段会产生越来越多的子类,这些子类的产生增强了代码的复用性,但是觉给用户使用带来了麻烦,这事可以考虑使用Facade为用户提供简洁的接口。
第三:在维护一个遗留的大型系统时,这个系统可能已经非常难以维护和拓展,此时可以为该遗留系统创建一个简洁清晰的Facade接口,让新系统与Facade接口交互,而Facade负责向旧系统转发新系统的对相关功能的调用。