庫與框架無法幫助我們將應用組織成容易瞭解、容易維護、具有彈性的架構,所以需要設計模式。
模式不是發明,而是發現。模式不是代碼,而是方案。
以下模式被認爲是歷經驗證的OO設計經驗。
1. 觀察者模式
1) 類圖
2) 定義
定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它所有依賴者都會收到通知並自動更新。
3)設計原則
爲了交互對象之間的松耦合設計而努力。
4)實現原理
Subject主題類是事件的主導者,通過registerObserver()註冊上有需求的觀察者,
通過NotifyObserver()實時更新自己狀態並告知已經註冊的各位觀察者。
observer.update(); 由每位觀察者自行處理更新的數據。
觀察者構造函數裏指定主題,並調用該主題的registerObserver()方法實現綁定。
public class WeatherData implements Subject {
private ArrayList observers;
public WeatherData() {
observers = new ArrayList();
}
public void registerObserver(Observer o) {
observers.add(0);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
}
public class CurrentCondition {
private Subject weatherData;
public CurrentCondition (Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float t, float h, float p) {
display(t, h, p);
}
}
5)點評
如果讓觀察者去“拉”主題的數據,這樣主題會門戶大開,被大肆挖掘數據,不夠安全。
所以更常用的設計是主題主動“推”送數據給觀察者,主題不關心觀察者的具體情況,實現松耦合。
6)應用場景
一對多的關係,多個對象實時監聽一個對象的狀態。
系統級更常見,比如建立一個對象,監聽按鍵,傳感器數據等。
2. 策略模式
1) 類圖
2) 定義
定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。
3)設計原則
針對接口編程,而不是針對實現編程。多用組合,少用繼承。
把系統中會變化的部分抽離出來封裝。
4)實現原理
在這個類中加一個接口,把行爲委託給這個接口處理,
在這個接口下有各種不同的策略實現它的行爲,
客戶只要通過set和get動態地設置變量就可以在運行時引用正確的策略。
public abstract class Duck {
FlyBehavior flyBehavior;
public Duck () {
}
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public FlyBehavior getFlyBehavior() {
return flyBehavior;
}
public void performFly () {
flyBehavior.fly();
}
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
... ...
}
}
5)點評
策略模式使用委託模型,增加了對象數量,使代碼較複雜,但使用對象組合,所以更具有彈性。
6)應用場景
3. 狀態模式
1) 類圖
基本同策略模式,唯一區別在於委託的狀態是一個抽象類而不是接口。
2) 定義
允許對象在內部狀態改變時改變它的行爲,對象看起來好像修改了它的類。
3)點評
將一羣行爲封裝在狀態對象中,context的行爲隨時可委託到那些狀態對象中的一個,
狀態是用在Context中來代表它的內部狀態以及行爲的,客戶不會直接改變context的狀態,改變狀態是方案中事先定義好的。
4)應用場景
適用於同一行爲在不同條件下產生不同表現的場景,把條件看成狀態,或者策略。4. 裝飾者模式
1) 類圖
2) 定義
動態地將責任附加到對象身上,若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
3)設計原則
類應該對擴展開放,對修改關閉。
4)實現原理
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public double cost(){
return .20 + beverage.cost();
}
}
5)點評
裝飾者與被裝飾者必須是同一類型,用繼承達到類型匹配。
裝飾者模式的設計中加入了大量的小類,會讓程序變得複雜難懂。
6)應用場景
對象須要添加一個新功能,新功能具有彈性,易於修改更新替代或疊加,同時不會改變原有對象。
典型例子比如JAVA I/O裏的讀寫功能。
5. 適配器模式
1) 類圖
2) 定義
將一個類的接口,轉換成客戶期望的另一個接口,適配器讓原本接口不兼容的類可以合作無間。
3)實現原理
客戶通過目標接口調用適配器的方法對適配器發出請求。
適配器把請求轉換成被適配者的接口。
客戶接收到調用的結果,但並未察覺適配器在中間的作用。
public class EnumerationIterator implements Iterator
{
Enumeration enum;
public EnumerationIterator(Enumeration enum) {
this.enum = enum;
}
public boolean hasNext() {
return enum.hasMoreElements();
}
public Object next() {
return enum.nextElement();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
4)點評
如果不用適配器,客戶就必須改寫代碼來調用新的接口。
適配器允許客戶使用新的庫和子集合而無須改變代碼,由適配器負責轉換。
5)應用場景
有新的客戶需求,不想改變客戶代碼,又想保留原有系統接口,那麼考慮用適配器模式作中間層轉換。6. 外觀模式
1) 類圖
2)定義
提供了一個統一的接口,用來訪問子系統中的一羣接口,外觀定義了一個高層接口,讓子系統更容易使用。
3)設計原則
“最少知識”原則:只和你的密友談話。
就任何對象而言,在該對象的方法內,我們只應該調用屬於以下範圍的方法:
- 該對象本身
- 被當做方法的參數而傳遞進來的對象
- 此方法所創建或實例化的任何對象
- 對象的任何組件
5)應用場景
某對象具備複雜的功能,客戶希望使用簡單的高層接口。
典型的例子如家庭影院系統。
7. 工廠方法模式
1) 類圖
2) 定義
定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個,工廠方法讓類把實例化推遲到子類。
3)設計原則
依賴倒置原則:要依賴抽象,不要依賴具體類。
不能讓高層組件依賴低層組件,而且兩者都應該依賴於抽象。
遵循倒置原則應注意:
- 變量不可以持有具體類的引用
- 不要讓類派生自具體類
- 不要覆蓋基類中已實現的方法
4)實現原理
首先聲明一個工廠方法factroyMethod(),工廠方法將客戶和實際創建具體產品的代碼分隔開來。
子類工廠繼承抽象工廠,子類工廠的工廠方法具體實現低層組件-產品。
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box;
return pizza;
}
abstract Pizza createPizza(String type);
}
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else
return null;
}
}
客戶只要實例化具體的子類工廠,讓子類工廠生產產品。public static void main () {
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
}
5)點評
工廠方法讓子類決定要實例化的類是哪一個,選擇使用哪個子類就決定了實際創建哪個產品。
將創建對象的代碼集中在一個對象或方法中,可以避免代碼的重複,且方便維護。
客戶在實例化對象時,只會依賴於接口,而不是具體類。
除了工廠方法模式,還有抽象工廠模式,有利於創建對象的家族。
8. 單件模式
1) 定義
確保一個類只有一個實例,並提供一個全局訪問點。
2)實現原理
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null)
uniqueInstance = new Singleton();
return uniqueInstance;
}
}
3)應用場景
獨一無二的資源,比如線程池,緩存,對話框,偏好,日誌,設備驅動等。
這些對象可能非常耗資源,而且初始化後程序一直沒有使用它。
9. 命令模式
1) 類圖
2) 定義
3)實現原理
關鍵要理清客戶,發起者Invoker,接收者Receiver三者的關係以及命令對象Command與具體動作receiver.Action()之間的關係。
- 客戶提出請求 - 客戶創建一個具體命令對象,這個命令對象暴露execute()接口
- 發起者封裝請求 - 發起者由setCommand()持有用戶提出的具體命令,並在某個時刻調用命令對象的execute(),實施動作
- 接收者實現請求 - 具體命令對象定義了動作receiver.Action()和接收者之間的綁定關係
調用者只要調用execute()就可以發出請求,由具體命令調用接收者的一個或多個動作。
public interface Command {
public void execute();
}
public class LightOnCommand implements Command { //具體命令
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on(); // 具體動作
}
}
public class SimpleRemoteControl { //調用者
Command slot;
public SimpleRemoteControl () {}
public void setCommand(Command command) { //調用者持有用戶的具體命令
slot = command;
}
public void buttonWasPressed() { //實施動作的時刻
slot.execute();
}
}
public class RemoteControlTest {
public static void main() {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light); //用戶創建具體命令
remote.setCommand(lightOn);
remote.buttonWasPressed();
}
}
4)點評
一旦有新的動作加入,發起者並不需要改變。
5)應用場景
典型例子:多功能on/off按鈕,記錄狀態可撤銷動作,使用宏命令的party模式
更多應用:隊列請求,日誌請求
10. 模板方法模式
1) 類圖
2) 定義
在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中,模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。
3)設計原則
好萊塢原則:別調用我們,我們會調用你。
4)點評
當子類必須提供算法中的某個方法或步驟的實現時,就使用抽象方法。
如果算法的這個部分是可選的,就用鉤子,要不要掛鉤由子類決定。
模板方法模式是最常用的模式。