Java設計模式研究

庫與框架無法幫助我們將應用組織成容易瞭解、容易維護、具有彈性的架構,所以需要設計模式。

模式不是發明,而是發現。模式不是代碼,而是方案。

以下模式被認爲是歷經驗證的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)設計原則

最少知識”原則:只和你的密友談話。

就任何對象而言,在該對象的方法內,我們只應該調用屬於以下範圍的方法:

  • 該對象本身
  • 被當做方法的參數而傳遞進來的對象
  • 此方法所創建或實例化的任何對象
  • 對象的任何組件

4) 點評
外觀模式沒有封裝於系統的類,外觀只提供簡單的接口,依然將系統完整的功能暴露出來。
外觀不只簡化了接口,也將客戶從組件的子系統中解耦。

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)點評

當子類必須提供算法中的某個方法或步驟的實現時,就使用抽象方法。

如果算法的這個部分是可選的,就用鉤子,要不要掛鉤由子類決定。

模板方法模式是最常用的模式。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章