我的武林祕籍設計模式之策略模式
先從簡單的鴨子游戲說起
jack為公司做了一套相當牛逼的鴨子游戲,鴨子能戲水,能呱呱叫。此係統內部設計使用了標準的oo技術,設計了一個鴨子超類,並讓鴨子繼承此超類
去年由於公司競爭壓力加劇,在為期一週的高爾夫假期兼頭腦風暴之下,公司主管認為是創新的時候了,他們要在下週網際網路大會上展示一些牛逼哄哄的東西振奮人心。
現在我們讓鴨子能飛
主管們相信,會飛的鴨子能將競爭者遠遠拋在後面。在這個時候,jack拍拍胸脯說,“領導放心,這個飛鴨我一個星期就能搞定”,畢竟,我是一個oo程式設計師,一點問題沒有。
jack在想,我只要在duck類中加上fly()方法,所有繼承的鴨子就都能飛了。這是我大顯身手的時候了,綻放我的oo才華吧!哈哈哈
但是,可怕的問題發生了
領導帶著jack所開發的鴨子游戲上大會上演講,許多鴨子就在螢幕上飛來飛去,臺下一片笑聲。在講臺上,領導很尷尬。大會結束,立馬打電話給jack。
jack在電話上承認他的設計有點問題,一旦給超類加上fly()方法,所有的鴨子都能飛了,連那些不具備飛行能力的鴨子也能飛了。但是jack又想,我可以讓那些不具備飛行能力的鴨子在他自己的類中覆蓋fly()方法,讓它什麼事情也不做,這樣鴨子就不能飛了。
利用介面如何?
jack意識到繼承不是好的解決方法,因為他拿到領導的備忘錄,裡面說是以後會常常更新產品,至於怎樣的需求還沒有想好。jack知道了規格會常常變更,每當有新鴨子子類出現時,他就要檢查所有子類的鴨子的fly()方法是否要覆蓋,要是還有其他方法的話也是同樣需要檢查,這簡直是噩夢。
所以他需要一個更清晰的方法,讓某些鴨子型別可飛或可叫等功能。
於是他想了一個方法把可變的功能性方法提取出來,比如把fly()方法從超類中取出,單獨設定成Flyable介面,quack()方法設定成QuackAble介面,讓會飛的鴨子就去實現flyable介面,會叫的鴨子實現Quackable介面。
領導看見了jack的這種設計模式,還是一臉不高興,雖然你用介面的方法去解決所有鴨子繼承的缺陷,但是使用介面的話,卻造成程式碼無法複用,這隻能是從一個噩夢跳進另一個噩夢,甚至,在會飛的鴨子中,飛行的動作也是千變萬化。
如果能有一種建立軟體的方法,好讓我們可以用一種對既有的程式碼影響最小的方式來修改軟體該有多好,我們就可以花較少的時間重做程式碼,而多讓程式做更酷的事情。
現在我們知道使用繼承不能很好的解決問題,因為鴨子的行為在子類裡不斷改變,並且讓所有鴨子都有這些行為是不恰當的。使用介面也是有問題的,因為在介面中,並沒有程式碼的實現,會造成程式碼不能複用,在每個實現的子類都要實現該方法,不可取。
設計原則一
找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的程式碼混在一起。
好了,該是把鴨子的行為從duck類中取出的時候了!
現在,為了要分開“變化和不會變化的部分”,我們準備建立兩組類,一個是fly相關的,一個是quack相關的,每一組類都將實現各自動作,比方說,我們可能有一個類實現“呱呱叫”,另一個類實現“吱吱叫”,還有一個類實現“安靜”。
設計原則二
針對介面程式設計,而不是針對實現程式設計
為了不讓鴨子子類直接實現行為介面,我們設計了一個行為介面類FlyBehavior和QuackBehavior 所表示的行為,所以實際的“實現”不會被綁死在鴨子的子類中。看類圖
接下來,有了以上的行為類,我們只要把這些行為類作為鴨子類的屬性,注入就行。
我們實現程式碼
/** * Created by huangx on 2018/11/12. */ public interface Flybehavior { public void fly(); }
/** * Created by huangx on 2018/11/12. */ public class FlyWithWings implements Flybehavior{ public void fly() { System.out.println("i am flying!"); } }
/** * Created by huangx on 2018/11/12. */ public class FlyNoWay implements Flybehavior{ public void fly() { System.out.println("i cant fly"); } }
/** * Created by huangx on 2018/11/12. */ public interface QuackBehavior { public void quack(); }
/** * Created by huangx on 2018/11/12. */ public class Quack implements QuackBehavior { public void quack() { System.out.println("quack"); } }
/** * Created by huangx on 2018/11/12. */ public class MuteQuack implements QuackBehavior{ public void quack() { System.out.println("silence"); } }
/** * Created by huangx on 2018/11/12. */ public abstract class Duck { Flybehavior flybehavior; QuackBehavior quackBehavior; public Duck(){ } public abstract void display(); public void performFly() { flybehavior.fly(); } public void performQuack(){ quackBehavior.quack(); } public void swim(){ System.out.println("All duck can float"); } }
/** * Created by huangx on 2018/11/12. */ public class MallardDuck extends Duck { // 屬性不僅能通過建構函式注入,也可以通過set方法注入,看你心情。 public MallardDuck(){ super.flybehavior=new FlyWithWings(); //這是可飛的鴨子 給他設定會飛的屬性 super.quackBehavior=new Quack(); //這是能叫喚的鴨子 給他設定會叫的屬性 } public void display(){ System.out.println("i am a real Mallard duck"); } }
/** * Created by huangx on 2018/11/12. */ public class TestDuck { public static void main(String[] args) { Duck mallard=new MallardDuck(); mallard.performFly(); mallard.performQuack(); } }
領導看見jack的這個設計,高興壞了,立馬打電話讓財務給jack加工資,併為她介紹女朋友!
這就是策略模式,策略模式的官方定義是他封裝了演算法族,讓它們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶。
結束了,這是我的第一篇技術blog,還希望以後我能繼續堅持寫下去!!!