今天來看下策略模式
以下內容大部分來自《設計模式》一書,僅做了整理。
定義:策略模式定義了算法族,分別封裝起來,讓他們之間可以相互替換。
我們來設計一個場景:一個關於鴨子的遊戲,鴨子在水裏邊游泳,邊呱呱叫着。
超類:Duck.class
/** * 鴨子超類 */ public abstract class Duck { /** * 鴨子會呱呱叫 */ public void qucak(){}; /** * 鴨子會游泳 */ public void swim(){}; /** * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的 */ public abstract void display(); }而鴨子的子類需要繼承Duck類,就可以實現呱呱叫,和游泳能力,只需要自己實現自己的外觀就可以了。
綠頭鴨:MallardDuck.class
/** * 綠頭鴨 */ public class MallardDuck extends Duck{ private static String TAG=MallardDuck.class.toString(); @Override public void display() { Log.i(TAG,"----------這是綠頭鴨---------"); } }紅頭鴨:RedHeadDuck.class
/** * 紅頭鴨 */ public class RedHeadDuck extends Duck { private static String TAG = RedHeadDuck.class.toString(); @Override public void display() { Log.i(TAG, "----------這是紅頭鴨---------"); } }如果現在需求有改動說是這些鴨子要會飛,我們只需要在超類裏添加一個fly方法就可以了,而子類並不需要做改動
/** * 鴨子超類 */ public abstract class Duck { /** * 鴨子會呱呱叫 */ public void qucak(){}; /** * 鴨子會游泳 */ public void swim(){}; /** * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的 */ public abstract void display(); /** * 飛行 */ public void fly(){}; }然後產品經理跟你說我們需要向裏面添加一種鴨子,叫橡皮鴨。現在問題就出現了,橡皮鴨是玩具,並不會飛,也不會呱呱叫。
橡皮鴨:RubberDuck.class
/** * 橡皮鴨 */ public class RubberDuck extends Duck{ private static String TAG = RubberDuck.class.toString(); /** * 橡皮鴨並不會呱呱叫,所以重寫父類呱呱叫方法 */ @Override public void qucak() { Log.i(TAG, "----------橡皮鴨不會呱呱叫---------"); } @Override public void display() { Log.i(TAG, "----------這是橡皮鴨---------"); } /** * 橡皮鴨也不會飛,重寫父類飛行方法 */ @Override public void fly() { Log.i(TAG, "----------橡皮鴨不會飛---------"); } }這樣看似是一個可行的方案,可是鴨子的種類越來越多,鴨子會叫的、不會叫的、會游泳的、不會游泳的。。。。,也可能會增加一個鴨子屬性,而已有的鴨子對這個屬性又有不同的屬性,我們就必須再去每一個鴨子子類裏去重寫父類的這個方法,這樣一直在重寫父類的方法,導致代碼重複,工作量越來越大,這並不是我們想要看到的方案。
如果能有一種能夠建立軟件的方法,能夠讓我們對既有的代碼影響最小的方式來改動軟件,我們就可以花更少的時間重做代碼,節省大量的工作量、工作時間那該多好。
不管一個軟件設計的多好,一段時間後,需求總會做改動。
現在可以看出繼承(extends)並不能很好的解決這個問題,因爲你不知道在未來子類會新冒出一個什麼屬性。
所有我們將引出“針對接口編程”概念。
“針對接口編程”的真正意思是“針對超類型(supertype)編程”
這裏所謂的“接口”有多個含義,接口是一個“概念”,也是一種Java的interface構造。你可以在不涉及Java interface的情況下,“針對接口編程”,關鍵就在多態。利用多態,程序可以針對超類型編程,執行時會根據實際狀況執行到真正的行爲,不會被綁死在超類型的行爲上。“針對超類型編程”這句話,可以明確的說成“變量的聲明類型應該是超類,通常是一個抽象類或者是一個接口,如此,只要是具體實現此超類型的類所產生的對象,都可以指定給這個變量。這也意味着,聲明類時不用理會以後執行時的真正對象類型!”
---------------------------------------------------------------------下面纔是正文------------------------------------------------------------------------------------------
上面這些話是書裏說的,理解不了就別看了,簡單的說就是把易變的部分做成接口(interface)形式,在父類裏聲明一個接口變量,在子類裏給這個變量賦值。
下面上代碼:
鴨子的呱呱叫和飛行行爲是易變的,我們就把呱呱叫和飛行行爲做成接口形式。
呱呱叫接口:QuackInterface.class
/** * 呱呱叫行爲接口 */ public interface QuackInterface { /** * 鴨子會呱呱叫 */ void qucak(); }飛行行爲接口:FlyInterface.class
/** * 飛行行爲接口 */ public interface FlyInterface { /** * 飛行 */ void fly(); }
以前的Duck.class重新寫
/** * 鴨子超類 */ public abstract class Duck { /** * 爲行爲接口聲明兩個引用變量 */ QuackInterface quackInterface;//呱呱叫行爲接口 FlyInterface flyInterface;//飛行行爲接口 /** * 鴨子會游泳 */ public void swim(){}; /** * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的 */ public abstract void display(); /** * 呱呱叫行爲委託給呱呱叫接口 */ public void perfromQuack(){ quackInterface.qucak(); } /** * 飛行行爲委託給飛行接口 */ public void perfromFly(){ flyInterface.fly(); } }綠頭鴨類重寫:MallardDuck.class
/** * 綠頭鴨 */ public class MallardDuck extends Duck implements QuackInterface,FlyInterface{ private static String TAG=MallardDuck.class.toString(); @Override public void display() { Log.i(TAG,"----------這是綠頭鴨---------"); } @Override public void qucak() { Log.i(TAG,"----------綠頭鴨呱呱叫---------"); } @Override public void fly() { Log.i(TAG,"----------綠頭鴨會飛---------"); } }橡皮鴨:RubberDuck.class
/** * 橡皮鴨 */ public class RubberDuck extends Duck implements QuackInterface,FlyInterface{ private static String TAG = RubberDuck.class.toString(); @Override public void display() { Log.i(TAG, "----------這是橡皮鴨---------"); } @Override public void qucak() { Log.i(TAG, "----------橡皮鴨不會呱呱叫---------"); } @Override public void fly() { Log.i(TAG, "----------橡皮鴨不會飛---------"); } }我們創建這些類之後,就可以使用了
Duck duck=new MallardDuck(); duck.perfromQuack(); duck.perfromFly();
Duck duck=new RubberDuck(); duck.perfromQuack(); duck.perfromFly();這樣就可以創建出會飛、會呱呱叫的綠頭鴨和不會飛、不會叫的橡皮鴨了。
-------------------------------------------------------------動態設定行爲,下面的是更好的方案---------------------------------------------------------------------------------------------------
先拿一個飛行屬性舉例。
在Duck.class中加入一個方法:
public void setFlyInterface(FlyInterface flyInterface) { this.flyInterface = flyInterface; }完整Duck.class
/** * 鴨子超類 */ public abstract class Duck { /** * 爲行爲接口聲明引用變量 */ FlyInterface flyInterface;//飛行行爲接口 /** * 鴨子會游泳 */ public void swim(){}; /** * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的 */ public abstract void display(); /** * 飛行行爲委託給飛行接口 */ public void perfromFly(){ flyInterface.fly(); } public void setFlyInterface(FlyInterface flyInterface) { this.flyInterface = flyInterface; } }新增兩個類
/** * 這個鴨子不會飛 */ public class NoFlyClass implements FlyInterface{ private static String TAG=NoFlyClass.class.toString(); @Override public void fly() { Log.i(TAG, "----------我不會飛---------"); } }
/** * 這個鴨子會飛 */ public class FlyClass implements FlyInterface{ private static String TAG=FlyClass.class.toString(); @Override public void fly() { Log.i(TAG, "----------我會飛---------"); } }重寫綠頭鴨類:MallarDuck.class
/** * 綠頭鴨 */ public class MallardDuck extends Duck{ private static String TAG=MallardDuck.class.toString(); @Override public void display() { Log.i(TAG,"----------這是綠頭鴨---------"); } }重寫橡皮鴨類:RubberDuck
/** * 橡皮鴨 */ public class RubberDuck extends Duck{ private static String TAG = RubberDuck.class.toString(); @Override public void display() { Log.i(TAG, "----------這是橡皮鴨---------"); } }使用的時候
Duck mallardDuck=new MallardDuck(); mallardDuck.setFlyInterface(new FlyClass()); mallardDuck.perfromFly();
Duck rubberDuck=new RubberDuck(); rubberDuck.setFlyInterface(new NoFlyClass()); rubberDuck.perfromFly();這篇文章到這就結束了。
如有錯誤或紕漏的地方,歡迎指正。