軟體構造系列學習筆記(5.3)————可複用性的設計模式
可複用性的設計模式
除了Framework,5-2節所討論的其他技術都過於“基礎”和“細小”,有沒有辦法做更大規模的複用設計?
本節:幾種典型的“面向複用”的設計模式。
目錄
- 介面卡模式(Adapter)
- 裝飾器模式(Decorator )
- 外觀模式(Facade)
- 策略模式(Strategy)
- 模板方法模式(Template method)
- 迭代器模式( Iterator)
除了類本身,設計模式更強調多個類/物件之間的關係和互動過程—比介面/類複用的粒度更大。
Structural patterns 結構型模式
結構型模式包含了前三種設計模式,包括介面卡模式,裝飾器模式和外觀模式。下面一一進行介紹。
介面卡模式(Adapter)
目的:將某個類/介面轉換為client期望的其他形式。
介面卡讓類可以協同工作,否則就會因為不相容的介面而無法工作。通過增加一個介面,將已存在的子類封裝起來,client面向介面程式設計,從而隱藏了具體子類。
物件:將舊元件重用到新系統(也稱為“包裝器”)。
來看下面一個例子:
如果不使用介面卡模式,會出現如下問題:
採用介面卡模式後就能解決這一問題:
通過這個例子我們就可以瞭解到介面卡模式的作用了。
裝飾器模式(Decorator)
裝飾器模式是為了解決以下問題出現的一個設計模式:為物件增加不同側面的特性。
在這種設計模式下,我們對每一個特性構造子類,通過委派機制增加到物件上。
考慮以下問題:
假設你需要Stack資料結構的各種擴充套件。
- UndoStack:一個允許你撤銷先前的push或pop操作的棧
- SecureStack:一個需要密碼的棧
- SynchronizedStack:一個序列化併發訪問的棧
我們可以採用繼承的方式來解決。
之後我們又需要任意可組合的擴充套件:
- SecureUndoStack:需要密碼的堆疊,並且還可以撤消以前的操作
- SynchronizedUndoStack:一個堆疊,用於序列化併發訪問,還可以讓您撤銷先前的操作
- SecureSynchronizedStack:…
- SecureSynchronizedUndoStack:…
我們又怎麼處理呢?繼承層次結構?多繼承?但是會顯得很麻煩,這時候裝飾器模式就可以很好地解決這一問題。
構建一個普通的堆疊:
Stack s = new ArrayStack();
構建撤消堆疊:
UndoStack s = new UndoStack(new ArrayStack());
構建安全的同步撤消堆疊:
SecureStack s = new SecureStack(
new SynchronizedStack(
new UndoStack(s))
裝飾器 vs. 繼承
- 裝飾器在執行時組成特徵;繼承在編譯時組成特徵。
- 裝飾器由多個協作物件組成;繼承產生一個明確型別的物件。
- 可以混合和匹配多個裝飾;多重繼承在概念上是困難的。
java.util.Collections
中也有一些裝飾器模式:
將mutable list 變為 immutable list:
static List<T> unmodifiableList(List<T> lst);
static Set<T> unmodifiableSet( Set<T> set);
static Map<K,V> unmodifiableMap( Map<K,V> map);
Similar for synchronization:
static List<T> synchronizedList(List<T> lst);
static Set<T> synchronizedSet( Set<T> set);
static Map<K,V> synchronizedMap( Map<K,V> map);
外觀模式(Facade)
外觀模式是為了解決客戶端需要通過一個簡化的介面來訪問複雜系統內的功能這一問題提出的。
意圖是提供一個統一的介面來取代一系列小介面呼叫,相當於對複雜系統做了一個封裝,簡化客戶端使用。便於客戶端學習,解耦 。
下面舉例來說明這一設計模式:
假設我們有一個具有一組介面的應用程式來使用MySql / Oracle資料庫,並生成不同型別的報告,如HTML報告,PDF報告等。
因此,我們將有不同的介面集合來處理不同型別的資料庫。現在客戶端應用程式可以使用這些介面來獲取所需的資料庫連線並生成報告。但是,當複雜性增加或介面行為名稱混淆時,客戶端應用程式將難以管理它。
因此,我們可以在這裡應用Facade模式,並在現有介面的頂部提供包裝介面以幫助客戶端應用程式。
Two Helper Classes for MySQL and Oracle:
A façade class:
客戶端程式碼:
可以看到採用了Facade設計模式的客戶端程式碼簡潔了許多,更方便客戶使用。
行為類模式
行為類模式包含了後三種設計模式,包括策略模式,模板模式和觀察模式。
策略模式( Strategy)
問題:針對特定任務存在不同的演算法,但客戶端可以根據動態上下文在執行時切換演算法。
示例:對客戶列表進行排序(氣泡排序,合併排序,快速排序)
解決方案:為演算法建立一個介面,併為演算法的每個變體建立一個實現類。
優點:
- 易於擴充套件新演算法實現
- 從客戶端上下文中分離演算法
另外一個例子:
模板模式(Template Method)
問題:幾個客戶共享相同的演算法,但具體細節不同,即演算法由可定製的部分和不變的部分組成。 常見的步驟不應該在子類中重複,但需要重新使用。簡而言之就是做事情的步驟一樣,但具體方法不同。
示例:
- 執行測試用例測試元件
- 開啟,閱讀和編寫不同型別的文件
解決方法:共性的步驟在抽象類內公共實現,差異化的步驟在各個子類中實現。
一般使用繼承和重寫實現模板模式。 模板模式在框架中廣泛使用。用UML圖描述見下圖。
接著我們看下面一個具體例子:
具體程式碼如下:
客戶端就可以這樣使用:
OrderProcessTemplate netOrder = new NetOrder();
netOrder.processOrder();
OrderProcessTemplate storeOrder = new StoreOrder();
storeOrder.processOrder();
迭代器模式(Iterator)
問題:客戶需要統一的策略來訪問容器中的所有元素,與容器型別無關
解決方案:迭代器策略模式
結果:
- 隱藏底層容器的內部實現
- 支援多個遍歷策略,介面統一
- 易於更改容器型別
- 便於部分程式之間的通訊
這種模式讓自己的集合類實現Iterable
介面,並實現自己的獨特Iterator
迭代器(hasNext, next, remove
),允許客戶端利用這 個迭代器進行顯式或隱式的迭代遍歷。UML圖如下:
Iterable
介面:實現該介面的集合物件是可迭代遍歷的
public interface Iterable<T> {
...
Iterator<T> iterator();
}
Iterator
介面:迭代器
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
具體例子見下圖: