讀書筆記
1. 前言
設(shè)計模式的存在,主要是方便代碼的可擴(kuò)展性和提高代碼的復(fù)用性钓株。在變化和穩(wěn)定之間尋找平衡點(diǎn)实牡。
2. 從鴨子說起
策略模式,書中是以鴨子為例轴合,進(jìn)行介紹的创坞。
假設(shè)鴨子的父類為:
Duck
quack()
swim()
display()
//鴨子的其他方法
因 為 每 一 種 鴨 子 的外 觀 都 不 同 , 所 以display()方法是抽象的,每個鴨子子類負(fù)責(zé)實(shí)現(xiàn)自己的display受葛。
3. 鴨子要飛了
現(xiàn)在需要給鴨子們摆霉,增加一個飛的動作。
新的需求來了奔坟,這時候我們要加入鴨子會飛的功能携栋。鴨子本來有翅膀,沒問題咳秉,在超類里加入一個fly方法婉支,讓所有的子類繼承就好了,
Duck
quack()
swim()
display()
fly() //新加入的飛行方法
//鴨子的其他方法
確實(shí)澜建,所有的鴨子都會飛了向挖,然而系統(tǒng)里還有一些橡皮鴨子蝌以,它們也會飛了,這是不符合常理的:沃8!
我們忽略了一件事情溶推,并不是所有子類都會飛徊件,某些不會飛的也繼承了此方法。
注意:對代碼所做的局部修改蒜危,影響層面可不只是局部虱痕。
當(dāng)涉及"維護(hù)"時,為了"復(fù)用"目的而使用繼承辐赞,結(jié)局并不完美部翘。
3.1 第一個解決辦法—子類覆蓋
我們可以把橡皮鴨中的fly方法覆蓋掉
RubberDuck
quack(){//吱吱叫}
display(){//橡皮鴨}
fly(){
//覆蓋,什么也不做
}
這樣好像確實(shí)解決了橡皮鴨的問題响委,但如果我們以后每次加入不會飛的鴨子新思,都要這樣檢查一遍,就失去了復(fù)用的意義了赘风。
3.2 第二個解決辦法---利用接口
我們可以把fly方法從超類中取出來夹囚,放進(jìn)一個Flyable接口中,這樣一來贝次,只有會飛的鴨子才實(shí)現(xiàn)此接口崔兴。
這樣會帶來很多重復(fù)代碼。
4. 重新看待問題
現(xiàn)在我們知道使用繼承并不能很好地解決問題,因?yàn)轼喿拥男袨樵谧宇惱锊粩嗟馗淖?并且讓所有的子類都有這些行為是不恰當(dāng)?shù)摹?br> Flyable與Quackable接口一開始似乎還挺不錯,解決了問題(只有會飛的鴨子才繼承Flyable),但是Java接口不具有實(shí)現(xiàn)代碼,所以繼承接口無法達(dá)到代碼的復(fù)用蛔翅。這意味著: 無論何時你需要修改某個行為,你必須得往下追蹤并在每一個定義此行為的類中修改它,一不小心,可能會造成新的錯誤!
幸運(yùn)的是,有一個設(shè)計原則,恰好適用于此狀況敲茄。
設(shè)計原則: 找出應(yīng)用中可能需要變化之處,把它們獨(dú)立出來,不要和那些不需要變化的代碼混在一起。
在變化和穩(wěn)定之間尋找平衡點(diǎn)山析。
好,該是把鴨子的行為從Duck類中取出的時候了!
5. 重新設(shè)計鴨子
我們提取出兩組類堰燎,一個是fly,一個是quack笋轨,每一組類實(shí)現(xiàn)各自的動作秆剪,可以指定特定類型的飛行行為給鴨子,讓它們動態(tài)的去改變就好了爵政。
設(shè)計原則:針對接口編程仅讽,而不是針對實(shí)現(xiàn)編程。
我們利用接口代表每個行為钾挟,行為的每個實(shí)現(xiàn)都將實(shí)現(xiàn)其中的一個接口洁灵。
這么一來,有了繼承的“復(fù)用”好處,卻沒有繼承所帶來的包袱。
6. 實(shí)現(xiàn)鴨子行為
首先掺出,在Duck類中“加入兩個實(shí)例變量”徽千,分別為“flyBehavior”與“quack Behavior”苫费,聲明為接口類型(而不是具體類實(shí)現(xiàn)類型),每個鴨子對象都會動態(tài)地設(shè)置這些變量以在運(yùn)行時引用正確的行為類型(例如:FlyWithWings、Squeak等)双抽。
我們也必須將Duck類與其所有子類中的fly()與quack()刪除,因?yàn)檫@些行為已經(jīng)被搬到FlyBehavior與QuackBehavior類中了百框。
我們用兩個相似的方法performFly()和performQuack()取代Duck類中的fly()與quack()。
//父類
Duck
FlyBehavior flyBehavior
QuackBehavior quackBehavior
performQuack(){ quackBehavior.quack(); }
swim()
display()
performFly()
//某個子類
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println(“I’m a real Mallard duck”);
}
}
7. 總結(jié)
當(dāng)你將兩個類結(jié)合起來使用牍汹,如同本例一般铐维,這就是組合(composition)。這種做法和“繼承”不同的地方在于,鴨子的行為不是繼承來的柑贞,而是和適當(dāng)?shù)男袨閷ο蟆敖M合”來的方椎。
如你所見聂抢,使用組合建立系統(tǒng)具有很大的彈性钧嘶,不僅可將算法族封裝成類,更可以“在運(yùn)行時動態(tài)地改變行為”琳疏,只要組合的行為對象符合正確的接口標(biāo)準(zhǔn)即可有决。
設(shè)計原則:多用組合,少用繼承空盼。
策略模式
定義了算法族书幕,分別封裝起來,讓它們之間可以互相替換揽趾,此模式讓算法的變化獨(dú)立于使用算法的客戶贬养。