一、前言

本篇主题为结构型模式中的第五个模式–外观模式。上篇 Java 设计模式主题为《Java 设计模式之组合模式(九)》

二、简单介绍

2.1 定义

外观(Facade)模式又称门面模式,为一组具有类似功能的类群,比如类库,子系统等等,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

2.2 参与角色

  1. Facade:调用方定义简单的调用接口。

  2. Subsystemclasses:功能提供者。指提供功能的类群(模块或子系统)。

2.3 应用场景

  1. 当需要构建一个层次结构的子系统时,使用外观模模式定义子系统中每层的入口点。

  2. 客户程序与抽象类的实现部分之间存在着很大的依赖性。

三、实现方式

我们以买股票为例,老王要购买股票 A 和股票 B。代码实现如下:
股票:

public class StockA {
    public void buy() {
        System.out.println("买入股票A");
    }
    public void sell() {
        System.out.println("卖出股票A");
    }
}
public class StockA {
    public void buy() {
        System.out.println("买入股票A");
    }
    public void sell() {
        System.out.println("卖出股票A");
    }
}
public class StockB {
    public void buy() {
        System.out.println("买入股票B");
    }
    public void sell() {
        System.out.println("卖出股票B");
    }
}
public class StockB {
    public void buy() {
        System.out.println("买入股票B");
    }
    public void sell() {
        System.out.println("卖出股票B");
    }
}

我们将 StockAStockB 看作是不同的系统/项目。
客户端:

public class Client {
    public static void main(String[] args) {
        StockA stockA = new StockA();
        StockB stockB = new StockB();
        // 买入
        stockA.buy();
        stockB.buy();
        // 卖出
        stockA.sell();
        stockB.sell();
    }
}

老王购买 2 支股票需要操作 2 个系统,进行买入和卖出的操作。
通过之前的学习我们知道客户端代码存在一定的问题:

  1. 违背单一职责原则。

  2. 与多个 API 耦合度高。

假设老王需要购买第三只股票或者更多,他得了解这些股票的行情以及学习在新的股票系统进行购买和卖出的操作流程。显然,这增加了老王的学习成本,同时也不利于老王对众多股票的操作。在软件设计中,这又给客户端和其他 API 造成新的耦合。
俗话说,专业人做专业事,老王可以把钱交给股票经理人,让股票经理人按照老王的要求去操作股票,至于怎么购买股票与何时卖出股票则是股票经理人该考虑的事情。其中,股票经理人就是 Facade
Facade

public class StockManager {
    private StockA stockA;
    private StockB stockB;
    public StockManager() {
        stockA = new StockA();
        stockB = new StockB();
    }
    public void buyStock() {
        this.stockA.buy();
        this.stockB.buy();
    }
    public void sellStock() {
        this.stockA.sell();
        this.stockB.sell();
    }
}

客户端:

public class Client {
    public static void main(String[] args) {
        StockManager sm = new StockManager();
        // 买入
        sm.buyStock();
        // 卖出
        sm.sellStock();
    }
}

这样,客户端代码与其他 API 耦合降低,代码也简化了许多。
UML 类图表示如下:

作者:月光中的污点

来源:https://www.extlight.com/2017/11/20/Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E5%8D%81%EF%BC%89/