模擬鴨子游戲的需求
SimUDuck
游戲中會出現(xiàn)各種鴨子莱坎,一邊游泳戲水玫氢,一邊呱呱叫。
通過標準的OO技術讲仰,設計一個超類慕趴。
需求增加
此程序需要會飛 的鴨子競爭者拋在后頭
方法一:在 Duck 超類加上 fly() 方法
但問題發(fā)生了:并非 Duck 所有的子類都會飛(玩具鴨子)
注意:當涉及到維護是,為了“復用”(reuse)目的而使用繼承鄙陡,結局并不完美
方法二:覆蓋超類的方法
當有新的鴨子出現(xiàn)冕房,要檢查每個鴨子可能要覆蓋 fly() 和 quark() ...
- 利用繼承來提供行為,導致的缺點
- 代碼在多個子類中重復
- 運行時的行為不容易改變
- 很難知道所有鴨子的全部行為
- 改變引發(fā)全身趁矾,造成其他鴨子不想要的改變
方法三:利用接口
可以把 fly() 從超類中提取出來耙册,放進一個“Flyable接口”中。這么一來毫捣,只有會飛的鴨子才實現(xiàn)此接口详拙。
看似不錯(只適應用不多,且飛的方式不同的情況)蔓同,但是在會飛的鴨子種類很多后饶辙,要稍微修改一下飛行的行為,就是一個災難...
分析
- 不是所有的子類都具有飛行和呱呱叫的行為斑粱,所以繼承不合適弃揽。
- 雖然 Flyable 和 Quackable 可以解決“一部分”問題,但是卻造成代碼無法復用,這只是從一個坑進入另一個坑矿微。
- 甚至痕慢,在會飛的鴨子中,飛行動作可能還有多種變化涌矢。掖举。。
解決之道--“采用良好的OO軟件設計原則”
軟件開發(fā)的一個不變的真理 --> 需求和改變
第一個設計原則:找出應用中可能需要變化之處娜庇,把它們獨立出來拇泛,不要和那些不需要變化的代碼混在一起
把會變化的部分取出來并“封裝”起來,好讓其它部分不會受到影響
為了把這兩個行為從Duck 類中分開思灌,將它們從 Duck 類中取出來俺叭,建立一組新類來代表每個行為。
設計鴨子的行為
讓鴨子類中的行為可以動態(tài)的改變就好了泰偿。我們應該在鴨子類中包含設定行為的方法熄守,這樣就可以在“運行時”動態(tài)的“改變”飛行行為。
第二個設計原則:針對接口編程耗跛,而不是針對實現(xiàn)編程
針對超類型編程
利用接口代表每個行為裕照,比方說 FiyBehavior 和 QuackBehavior ,而行為的每個實現(xiàn)都將實現(xiàn)其中的一個接口调塌。由行為類而不是Duck類來實現(xiàn)接口晋南。
- 以前的做法:行為來自 Duck 超類的具體實現(xiàn),或是繼承某個接口并由子類自行實現(xiàn)而來羔砾。都依賴于“實現(xiàn)”负间,被實現(xiàn)綁的死死的,沒辦法更改行為(除非寫更多代碼)姜凄。
vs
- 新設計中:鴨子的子類將使用接口(FlyBehavior 與 QuackBehavior)所標示的行為政溃,實際的“實現(xiàn)”不會被綁死在鴨子的子類中。即特定的具體行為編寫在實現(xiàn)了FlyBehavior 與 QuackBehavior 的子類中态秧。
這樣的設計董虱,可以放飛行和呱呱叫的動作被其他的對象復用,因為這些行為已經(jīng)與鴨子類無關了申鱼。
而我們可以新增一些行為愤诱,不會影響到既有的行為類,也不會影響“使用”到飛行行為的鴨子類捐友。
整合鴨子的行為
關鍵在于:鴨子現(xiàn)在會將飛行和呱呱叫的動作“委托”(delegate)別人處理淫半,而不是使用定義在 Duck 類(或子類)內(nèi)的呱呱叫和飛行方法。
做法:
- 首先楚殿,在 Duck 類中加入“兩個實例變量”撮慨,分別為“flyBehavior”與“quackBehavior”,聲明為接口類型(而不是具體類實現(xiàn)類型)脆粥,每個鴨子的對象都會動態(tài)地設置這些變量以在運行時引用正確的行為類型砌溺。
- 實現(xiàn) performQuack();
public class Duck{
QuackBehavior quackBehavior变隔;
// 還有更多
public void performQuack(){
quackBehavior.quack()规伐;//鴨子對象不親自處理呱呱叫行為,而是委托給quackBehavior引用的對象匣缘。
}
}
- 如何設定 flyBehavior 與 quackBehavior的實例變量
public class MallardDuck extends Duck{
public MallardDuck(){
quackBehavior = new Quack()猖闪;
flyBehavior = new FlyWithWings();
}
//別忘了,因為 MallarDuck 繼承了 Duck 類肌厨,所以具有 flyBehavior 與 quackBehavior 實例變量
}
我們正在構造器里面制造了具體的Quack實現(xiàn)類的實例(在后面的模式中培慌,可以修正這一點)
動態(tài)設定行為
通過 set 方法來設定鴨子的行為,而不是在鴨子的構造器內(nèi)實例化柑爸。
Duck 類中 加入 setFlyBehavior(FlyBehavior fly)吵护;