讓咱們先從一個簡單的鴨子模擬器開始講起。java
假設有個簡單的鴨子模擬器,遊戲中會出現各類鴨子,此係統的原始設計以下,設計了一個鴨子超類,並讓各類鴨子繼承此超類。算法
若此時咱們有了一個新的需求,咱們須要鴨子會飛,那麼咱們該如何修改代碼呢?編程
最初,咱們想在基類上加上fly方法,使得全部子類鴨子都擁有相應的fly方法。但這樣錯誤產生了,即便是本不應會飛的橡皮鴨子也擁有了fly方法。設計模式
或許咱們能夠把橡皮鴨中的fly方法覆蓋掉,但這樣每次新加入的不會飛的新鴨子類型,難道都要額外覆蓋一次fly方法嗎?太麻煩了。ide
利用繼承來提供duck的行爲,會致使運行時的行爲不容易改變,且改變容易牽一髮動全身。學習
那麼,利用接口如何?this
若常常須要更新產品,那麼每次覆蓋fly簡直是噩夢。那麼,咱們將fly單獨寫成一個接口,只有會飛的鴨子實現這個接口如何?設計
但這樣其實重複的代碼會變得很是多,形成fly代碼沒法複用,每一個會飛的鴨子都要實現fly方法。3d
那麼咱們該如何解決這個問題?在使用設計模式以前,不妨先求索於OO原則!code
軟件開發中,什麼是永恆真理?
惟一不變的是變化自己——約翰遜·斯賓塞
如今咱們已經知道了繼承沒法很好的解決問題,由於鴨子的行爲在子類中不斷改變,而且有的行爲子類不該該擁有。使用接口初看挺不錯的,但繼承接口沒法達到代碼的複用。這意味着,不管合適你須要修改某個行爲,你必須向下追蹤並在每個定義此行爲的類中修改它。
但還好,有一個OO設計原則正好適用於此種狀況:
找出系統中可能須要變化之處,把他們獨立出來,不要和那些不變化的代碼堆在一塊兒。
也就是把會變化的部分取出來,好讓其餘部分不會受此影響。
把會變化的部分取出來並封裝,之後能夠輕易地改動或擴充此部分,而不影響其餘不須要變化的部分。
那麼,如今是時候把鴨子的行爲從Duck類中取出了。
分開變化和不會變化的部分
目前而言,除了fly()和quack()之外,duck類其餘部分看起來不怎麼變更,因此咱們僅作些小改變。
爲此,咱們準備創建兩組類,一個是和fly相關的,另外一個和quack相關的,每一組類都實現各自的動做。
設計鴨子的行爲
如何設計
那組實現飛行和叫聲的類呢?咱們但願一切能有彈性,而且可以將行爲指定到鴨子的實例。而且可讓鴨子的行爲動態的改變。
有了這些目標要實現,咱們看第二個設計原則
針對接口編程,而不是針對實現編程。
從如今開始,鴨子的行爲將被放在分開的類中,此類專門提供某行爲接口的實現。這樣,鴨子類就再也不須要知道行爲的具體細節。
此次鴨子類不會負責實現flying和quacking接口,而是由咱們製造一組其餘類專門實現flybehavior和quackbeavior,這就稱爲行爲類,由行爲類而不是duck類來實現行爲接口。
這種作法和以往不一樣,以往是行爲來自於duck超類的具體實現,或是繼承某個接口並由子類自行實現而來。這兩種方法都是依賴於實現,無法變動行爲。
在咱們的新設計中,鴨子的子類將使用接口所表示的行爲,因此具體的實現不會被綁定在鴨子的子類中。
整合鴨子的行爲
關鍵在於,鴨子會將飛行和叫聲行爲委託給其餘對象處理。而不是由本身定義。
在Duck類中加入flyBehavior和quackBehavior變量,聲明爲接口類型。每一個鴨子對象動態設置這些變量以在運行時引用正確的行爲類型。
代碼以下
public interface FlyBehavior { public void fly(); }
public interface QuackBehavior { public void quack(); }
public class FlyWithWings implements FlyBehavior{ @Override public void fly() { System.out.println("I'm flying"); } }
public class FlyNoWay implements FlyBehavior{ @Override public void fly() { System.out.println("I can't fly"); } }
public class Quack implements QuackBehavior{ @Override public void quack() { System.out.println("quack!"); } }
public class MuteQuack implements QuackBehavior{ @Override public void quack() { System.out.println("<<silence>>"); } }
public class Squeak implements QuackBehavior{ @Override public void quack() { System.out.println("Squeak!"); } }
public abstract class Duck { protected FlyBehavior flyBehavior; protected QuackBehavior quackBehavior; abstract void display(); public void performFly() { flyBehavior.fly(); } public void performQuack() { quackBehavior.quack(); } }
public class MallardDuck extends Duck{ @Override public void display() { System.out.println("I'm a real mallard duck"); } public MallardDuck(){ flyBehavior = new FlyWithWings(); quackBehavior = new Quack(); } }
public class MiniDuckSimulator { public static void main(String[] args) { Duck mallardDuck = new MallardDuck(); mallardDuck.performFly(); mallardDuck.performQuack(); } }
動態設定行爲
在鴨子子類中爲兩個behavior加入set方法,而不是在構造器中進行實例化。有了這個,咱們就能在運行時隨時改變鴨子的行爲。
public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; }
總體設計
如今咱們來看看總體結構
咱們再也不把鴨子的行爲說成是行爲,而是一族算法。算法表明鴨子能作的事情。在本例中,咱們鴨子的行爲是組合來的,而不是繼承來的。
咱們獲得第三個OO設計原則
多用組合,少用繼承
學習完以上部分,咱們正式定義策略模式