Head First Design Parttern學習筆記(to be continued)

一、策略模式(Strategy Pattern)

設計一個父類Duck,定義並實現了fly()和quack()方法,定義abstract方法Display。子類MallardDuck和RedheadDuck繼承Duck,並實現各自的Display方法。如下圖:

image 圖1

1 繼承的缺點

step 1:增加子類RubberDuck(橡皮鴨),繼承Duck。如下圖:

image 圖2

於是問題1出現了:橡皮鴨會fly(),會quack()(嘎嘎叫)。---------------------------

繼承缺點1:父類的非abstract方法,會作用於所有子類。實現代碼重用性的同時,造成了極大的不靈活,甚至錯誤。

step 2:在RubberDuck中,覆蓋fly()和quack(),使之不會飛,叫聲爲squeak(吱吱叫)。如下圖:

image 圖3

出現問題2:我想增加DecoyDuck(),誘餌鴨,誘餌鴨是木頭假鴨,不會飛也不會叫。於是又得覆蓋fly方法,內容完全與RubberDuck的fly方法一樣,代碼重複;再覆蓋quack,不叫。如果我想在增加動力鴨呢,不會叫但會飛。。。。。。可怕的代碼重複。

繼承缺點2:如對父類的方法覆蓋來實現個性化,造成代碼在各子類中重複。

繼承缺點3:無盡的覆蓋,導致代碼一致性差,很難知道所有鴨子的全部行爲。

step 3:我想增加一頭貪吃鴨,平時不叫,想吃東西時叫。出現問題3:oh,如何動態改變鴨子的行爲?

繼承缺點4:在運行時很難改變行爲。

2 利用接口如何?

把fly和quack從Duck父類中剝離出來,分別放進flyable和quackable接口。這樣一來,只有會飛的鴨子來實現flyable接口,會叫的鴨子來實現quackable接口。如下圖:

image 圖4

這真是一個笨主意,重複的代碼更多。因爲每個會飛或會叫的鴨子子類,都得去實現一遍flyable或quackable方法。

3 解決之道

設計原則一:找出應用中可能需要變化之處,把他們獨立出來,不要和那些不需要變化的代碼混在一起。

fly和quack行爲,會隨鴨子的不同而改變。所以我們將把他們從Duck類中取出來,建立一組新類來代表每個行爲。

設計原則二:針對接口編程,而不是針對實現編程。

我們利用接口代表每個行爲,比方說,FlyBehavior與QuackBehavior,而行爲的每個實現都將實現其中的一個接口。鴨子類不負責實現flyable和quackable接口,而是由我們製造一組其他類專門來實現FlyBehavior和QuackBehavior,這就稱爲“行爲”類。這樣,我們就把鴨子和行爲分離開來,而以前的做法是:行爲來自與父類的具體實現,或是子類的覆蓋實現。這兩種做法都是依賴於“實現”,我們想描述的的鴨子,被實現綁得死死的。

實質上,針對接口編程即是捨棄繼承,使用組合。即是把各個行爲的具體實現(行爲爲可變部分)組合成一種特性,賦給對應的Duck。如把FlyBehavior的不會飛的實現,和QuackBehavior的squeak(吱吱叫)實現,組合起來,和Duck父類和在一起,表示橡皮鴨。

於是,我們整合鴨子的行爲如下:

Duck

FlyBehavior flyBehavior
QuackBehavior quackBehavoior
performQuack()
performFly()
swim()
display()

public class Duck{

QuackBehavior quackBehavior;

//還有更多

public void performQuack(){

          quackBehavior.quack();

}

}

public class MallardDuck extends Duck{

    public MallardDuck(){

    quackBehavior = new quack();

    flayBehavior = new FlyWithWings();

}

public void display(){

    System.out.println(“I’m a real Mallard duck”);

}

}

4 動態設定鴨子行爲,增加2個方法:

Duck

FlyBehavior flyBehavior
QuackBehavior quackBehavoior
performQuack()
performFly()
swim()
display()
setFlyBehavior()
setQuackBehavior()

public void setFlyBehavior(FlyBehavoir fb){

    flyBehavior = fb;

}

setQuackBehavior()類似。

5 封裝行爲的大局觀:

我們把可變的一組行爲,如Quack,叫做一族算法,加以封裝。鴨子可以不關注算法的具體實現,只是委託算法去做某種行爲即可。如委託Quack算法去吱吱叫。

鴨子的不變部分通過繼承超類得到,鴨子的可變部分,通過組合所需要的行爲對象得到。這裏用到了以下原則:

設計原則3:多用組合,少用繼承

策略模式定義了算法族,分別封裝起來,讓他們之間可以相互替換,此算法讓算法的變化,獨立於使用算法的客戶。

綜上,該示例設計如下圖:

image

圖5

http://wickedlysmart.com/headfirstdesignpatterns/code.html (souce code download address)

6 C語言的策略模式---鴨子的C實現

image

 

image

 

二、觀察者模式(Observer Pattern)

觀察者模式:定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,其他所有依賴者都會收到通知並自動更新。

出版者(Subject)+訂閱者(Observer)=觀察者模式。

定義觀察者模式:類圖

image 圖6

Subject是被依賴方,Observer是依賴方;Subject擁有數據並提供訪問數據的方法,Observer使用Subject提供的方法訪問數據,獲取和使用這些數據。Subject負責數據的更新,並通知Observer,Observer接收到數據有更新的通知後,自己決定是否要去獲取新數據,並進行相應動作。

鬆耦合的威力:當兩個對象之間的鬆耦合,他們依然可以交互,但是不太清楚彼此間的細節。觀察者提供了一種對象設計,讓主題和觀察者之間鬆耦合。

改變主題或觀察者之間的任何一方,並不會影響另一方。因爲兩者是鬆耦合的,所以只要他們之間的接口仍被遵守,我們就可以自由改變他們。

設計原則4:爲了交互對象之間的鬆耦合設計而努力。

鬆耦合的設計之所以能讓我們建立有彈性的OO系統,能夠應對變化,是因爲對象之間的互相依賴性降到了最低。

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