介紹
- 1、使用頻率高,常見的是第三方庫。
- 2、對外可見的只有一兩個類,而內(nèi)部涉及可能是個很龐大很復(fù)雜的系統(tǒng)。
定義
- 1、 要求子系統(tǒng)的外部與其內(nèi)部的通信必須通過一個統(tǒng)一的對象進(jìn)行。
- 2、 提供一個高層次的接口,使得子系統(tǒng)更易于使用。
使用場景
- 1、某個模塊很龐大或很復(fù)雜,希望外部能很簡單就使用。
- 2、希望能把控入口,外部只能通過某個類來訪問,這樣以后要換方案就不會影響外部使用。
簡單點(diǎn):一個模塊很復(fù)雜,但我們使用只需要一個類簡單操作就可以了。

再來舉個例子吧

A:金礦是怎么挖出來的?
B:礦工到礦井里就挖出來的唄
在外部只知道這些,但其實(shí)涉及到很多功能模塊,這就是「外觀模式」,讓別人以為就是這么簡單。
先看下涉及到的一些名詞:
- Dwarven 矮人(霍比特中就是矮人來挖金礦的) DwarvenMineWorker 矮人曠工
- DwarvenTunnelDigger 挖隧道矮人
- DwarvenGoldDigger 挖金礦矮人
- DwarvenCartOperator 運(yùn)輸?shù)陌?/li>
我比較傾向于逆向看代碼,也就是先看到結(jié)果,在順藤摸瓜。那就先上結(jié)果:
public class App {
public static void main(String[] args) {
DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade();
facade.startNewDay();
facade.digOutGold();
facade.endDay();
}
}
很簡單,就是 new 一個 facade 出來,調(diào)用相應(yīng)的方法,「開始新的一天」-->「挖礦」-->「結(jié)束」,結(jié)果:
DwarvenMineWorker - Dwarf gold digger wakes up.
DwarvenMineWorker - Dwarf gold digger goes to the mine.
DwarvenMineWorker - Dwarf cart operator wakes up.
DwarvenMineWorker - Dwarf cart operator goes to the mine.
DwarvenMineWorker - Dwarven tunnel digger wakes up.
DwarvenMineWorker - Dwarven tunnel digger goes to the mine.
DwarvenGoldDigger - Dwarf gold digger digs for gold.
DwarvenCartOperator - Dwarf cart operator moves gold chunks out of the mine.
DwarvenTunnelDigger - Dwarven tunnel digger creates another promising tunnel.
DwarvenMineWorker - Dwarf gold digger goes home.
DwarvenMineWorker - Dwarf gold digger goes to sleep.
DwarvenMineWorker - Dwarf cart operator goes home.
DwarvenMineWorker - Dwarf cart operator goes to sleep.
DwarvenMineWorker - Dwarven tunnel digger goes home.
DwarvenMineWorker - Dwarven tunnel digger goes to sleep.
看到這個過程由三個類型參與的,順藤摸瓜,我們先看下 facade 類做了什么:
public class DwarvenGoldmineFacade {
private final List<DwarvenMineWorker> workers;
public DwarvenGoldmineFacade() {
workers = new ArrayList<>();
workers.add(new DwarvenGoldDigger());
workers.add(new DwarvenCartOperator());
workers.add(new DwarvenTunnelDigger());
}
public void startNewDay() {
makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE);
}
public void digOutGold() {
makeActions(workers, DwarvenMineWorker.Action.WORK);
}
public void endDay() {
makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP);
}
private static void makeActions(Collection<DwarvenMineWorker> workers,
DwarvenMineWorker.Action... actions) {
for (DwarvenMineWorker worker : workers) {
worker.action(actions);
}
}
}
簡單說明下,在構(gòu)造中添加了三個類型的工作者,在每個動作中都會調(diào)用工作者的具體操作。
接著我們拿一個曠工矮人來看下具體實(shí)現(xiàn):
public class DwarvenGoldDigger extends DwarvenMineWorker {
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenGoldDigger.class);
@Override
public void work() {
LOGGER.info("{} digs for gold.", name());
}
@Override
public String name() {
return "Dwarf gold digger";
}
}
可以看到只是實(shí)現(xiàn)了父類定義的抽象方法(顯示名稱、工作內(nèi)容),那么接著看下父類的實(shí)現(xiàn):
public abstract class DwarvenMineWorker {
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenMineWorker.class);
public void goToSleep() {
LOGGER.info("{} goes to sleep.", name());
}
public void wakeUp() {
LOGGER.info("{} wakes up.", name());
}
public void goHome() {
LOGGER.info("{} goes home.", name());
}
public void goToMine() {
LOGGER.info("{} goes to the mine.", name());
}
private void action(Action action) {
switch (action) {
case GO_TO_SLEEP:
goToSleep();
break;
case WAKE_UP:
wakeUp();
break;
case GO_HOME:
goHome();
break;
case GO_TO_MINE:
goToMine();
break;
case WORK:
work();
break;
default:
LOGGER.info("Undefined action");
break;
}
}
// 執(zhí)行任務(wù)
public void action(Action... actions) {
for (Action action : actions) {
action(action);
}
}
public abstract void work();
public abstract String name();
static enum Action {
GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK
}
}
這個父類中提供了:醒來、去礦井、工作、回家、睡覺的操作.
在回頭看下最開始,我們只是 DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade();然后就調(diào)用一些操作 facade.startNewDay() ,就讓整個礦場工作起來了。
對于外部, 可見的只是 DwarvenGoldmineFacade ,但內(nèi)部可能是很復(fù)雜的操作。
還有另一個使用場景:整個功能模塊用的 SDK 可能過不久換另一個 SDK 來實(shí)現(xiàn)。這時候我們也可以考慮使用「外觀模式」來控制外界訪問。更換時就不會影響到具體使用。
我們做開發(fā)時,一定要先做好需求分析,反復(fù)確認(rèn)需求之后,選擇適當(dāng)?shù)目蚣?。在使用別人的庫時,可以先自己封裝一回,使用自己封裝的類而不是直接使用別人的庫。這樣做得好處就是更換庫不至于全盤都要修改。
當(dāng)我們自己寫個復(fù)雜的功能或系統(tǒng)供外部(也可以是自己)使用時,也可以考慮使用外觀模式,讓外部使用更簡單,還能控制好出口。
參考:
- java-design-patterns
- Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)