no_mkd
一。什麼是外觀模式?
簡單的說,外觀模式是用來簡化接口的。
通常,我們覺得一個子系統不好用,可能是因為它提供的外部接口太接近低層組件,讓我們用起來感到很麻煩。
因為我們不需要知道內部細節,我們只想要一個「一鍵完成」功能,調用子系統的某個方法,它可能替我們完成了圖片預處理,而不是靠我們自己來調用灰度化方法、圖像增強算法、噪聲處理方法等等來一步步實現預處理。
如果子系統提供的接口太接近低層組件,不僅不易用,而且破壞了子系統的封裝(想調用子系統就必須了解其各個低層組件,我們被迫知道了太多不應該知道的細節。。)
二。舉個例子
假設有一個封裝好的老式洗衣機,它提供了這些對外接口:
package FacadePattern;/**
-
@author ayqy
-
定義洗衣機接口 / public interface Washer { /
- 公共部分
- */ //連接電源 public abstract boolean connectToPower();
/*
- 洗滌部分
- */ //打開左側的洗滌艙 public abstract void openLeftSide(); //打開注水口 public abstract void openWaterHole(); //開始注水 public abstract void startWaterInjection(); //停止注水 public abstract void stopWaterInjection(); //開始旋轉左側洗滌艙 public abstract void startWashing(); //停止旋轉左側洗滌艙 public abstract void stopWashing();
/*
- 脫水部分
- */ //打開右側的脫水艙 public abstract void openRightSide(); //開始旋轉右側脫水艙 public abstract void startDewatering(); //停止旋轉右側脫水艙 public abstract void stopDewatering();
/*
- 排水部分省略。。
- */ }
沒辦法,它實在太老了,不能滿足我們的現代生活,但我們又買不起新的自動化洗衣機,所以我們需要把它變成一個半自動的洗衣機,用節省下來的時間去寫代碼。。先看看我們是如何用老式洗衣機來洗衣服的:
package FacadePattern;/**
-
@author ayqy
-
使用老式洗衣機來洗衣服 */ public class Washing implements Washer{
public static void main(String[] args) { //創建洗衣機對象 Washer washer = new Washing(); //連接電源 if(washer.connectToPower()){ //打開洗滌艙 washer.openLeftSide(); /裝入髒衣服過程省略/ //打開注水口 washer.openWaterHole(); //開始注水 washer.startWaterInjection(); /等待 5 分鐘/ //停止注水 washer.stopWaterInjection(); /添加洗滌劑過程省略/ //開始洗滌 washer.startWashing(); /15 分鐘後停止/ washer.stopWashing();
/* * 脫水部分省略 * */ }}
/*
- 忽略下面自動生成的這些東西。。
- */ @Override public boolean connectToPower() { // TODO Auto-generated method stub return false; }
@Override public void openLeftSide() { // TODO Auto-generated method stub
}
@Override public void openWaterHole() { // TODO Auto-generated method stub
}
@Override public void startWaterInjection() { // TODO Auto-generated method stub
}
@Override public void stopWaterInjection() { // TODO Auto-generated method stub
}
@Override public void startWashing() { // TODO Auto-generated method stub
}
@Override public void stopWashing() { // TODO Auto-generated method stub
}
@Override public void openRightSide() { // TODO Auto-generated method stub
}
@Override public void startDewatering() { // TODO Auto-generated method stub
}
@Override public void stopDewatering() { // TODO Auto-generated method stub
} }
僅僅演示了一個洗衣服的過程,我們就調用了那麼多操作,而且我們必須知道這台洗衣機的內部細節,不然根本無法使用它。。洗衣服可能需要 60 分鐘(注水 + 洗滌 + 脫水 + 排水),在這期間我們幾乎什麼事情也做不了,只能蹲在洗衣機旁邊不停的操作機器
我想,我們可能需要一個遙控器,上面有 2 個按鈕:
- 洗滌
- 脫水
然後我們洗衣服的過程會變成:
- 撳洗滌按鈕,自動連接電源,自動打開洗滌艙,自動注水 5 分鐘,自動開始洗滌 15 分鐘
- 撳脫水按鈕,自動打開脫水艙,自動脫水 10 分鐘,自動排水,自動斷開電源
這簡直太棒了,我們可以在吃午餐前撳一下洗滌按鈕,吃完之後去把衣服拿到右側,再撳一下脫水按鈕,然後去上班,下午回來之後把衣服晾起來就好了(當然,手動把衣服從左側拿到右側的過程是避免不了的,畢竟它太老了,想變成全自動洗衣機是不可能的。。)
來看看我們自己做的遙控器:
package FacadePattern;/**
-
@author ayqy
-
遙控器(也就是所謂的外觀) */ public class WasherFacade { private Washer washer;
public WasherFacade(Washer washer){ this.washer = washer; }
/**
- 自動洗滌 */ public void washing(){ //連接電源 if(washer.connectToPower()){ //打開洗滌艙 washer.openLeftSide(); /裝入髒衣服過程省略/ //打開注水口 washer.openWaterHole(); //開始注水 washer.startWaterInjection(); /等待 5 分鐘/ //停止注水 washer.stopWaterInjection(); /添加洗滌劑過程省略/ //開始洗滌 washer.startWashing(); /15 分鐘後停止/ washer.stopWashing(); } }
/**
-
自動脫水 */ public void dewashing(){ /判斷是否已連接電源過程省略/ //打開右側的脫水艙 washer.openRightSide(); //開始旋轉右側脫水艙 washer.startDewatering(); //停止旋轉右側脫水艙 washer.stopDewatering();
/*
- 排水過程省略
- 切斷電源過程省略
- */ } }
有了遙控器之後,我們是這樣洗衣服的:
package FacadePattern;/**
-
@author ayqy
-
測試應用了外觀模式的半自動洗衣機(利用遙控器) */ public class Test implements Washer{
public static void main(String[] args) { //創建老式洗衣機 Washer washer = new Test(); //創建外觀(遙控器) WasherFacade facade = new WasherFacade(washer); //撳洗滌按鈕開始洗衣服 facade.washing(); /把衣服拿到另一側/ //撳脫水按鈕開始脫水 facade.dewashing(); }
/*
- 忽略下面這些自動生成的東西。。
- */ @Override public boolean connectToPower() { // TODO Auto-generated method stub return false; }
@Override public void openLeftSide() { // TODO Auto-generated method stub
}
@Override public void openWaterHole() { // TODO Auto-generated method stub
}
@Override public void startWaterInjection() { // TODO Auto-generated method stub
}
@Override public void stopWaterInjection() { // TODO Auto-generated method stub
}
@Override public void startWashing() { // TODO Auto-generated method stub
}
@Override public void stopWashing() { // TODO Auto-generated method stub
}
@Override public void openRightSide() { // TODO Auto-generated method stub
}
@Override public void startDewatering() { // TODO Auto-generated method stub
}
@Override public void stopDewatering() { // TODO Auto-generated method stub
} }
簡直輕鬆愜意,不過仔細一看,不就是定義了一個方法來封裝方法調用嘛,有什麼了不起的?與在我們的新項目代碼中建立定義兩個方法負責洗滌和脫水有什麼區別嗎?當然有,不要着急。
三。外觀模式的優點
-
低耦合
先看看我們的命名方式,遙控器叫做 WasherFacade,如果要劃分模塊,它應該與 Washer 放在一起吧。沒錯,通過定義 Facade,我們成功解耦了 Washer 與我們的代碼,意味著一旦 Washer 發生變更,我們直接修改 Facade 就好了,而不是在我們冗長的項目代碼裡尋找某兩個方法
-
保護了子系統的封裝
我們並沒有打開封裝好的 Washer 去修改,而是添加了一些代碼來簡化 Washer 的接口,以前調用者對 Washer 的內部很了解,但現在它對 Washer 幾乎一無所知(除構造方法外)
-
適用於含有多個不同對象的子系統
例子中的 Washer 只是一個單一對象,好像應該由 Washer 本身提供這樣的簡單接口。但如果要實現日常起居的半自動化,我們會面對多個對象,比如門,窗,窗簾,電視,微波爐,洗衣機,電腦等等。我們希望一鍵準備早餐(自動開燈,自動開啟微波爐加熱),一鍵午睡(自動關門,自動拉上窗簾,自動熄滅燈光)等等功能,外觀模式同樣適用:我們只需要讓外觀多持有幾個具體對象就好了
-
有效地簡化了子系統的接口
之前洗衣服需要蹲在洗衣機旁不停的操作,現在我們可以「一鍵完成」了,這才是我們想要的簡單易用的接口
-
滿足「最少知識原則」
我們做到了「只和朋友交談」,我們的新系統只認識 Facade,只和它交談,而不是跑去和子系統中的一大堆低層組件交談
四。總結
外觀模式,用來為複雜的子系統提供簡單易用的高層接口。
當你糾結於很多低層組件得不到解脫的時候,不妨去做一個遙控器,我想,你可能確實需要它。。
暫無評論,快來發表你的看法吧