鴨子游戲例子java
Joe上班的公司作了一套至關成功的模擬鴨子游戲:SimUDuck。遊戲中出現各類鴨子,一邊游泳戲水(swim),一邊呱呱叫(quack),注意是呱呱叫,而不是吱吱叫。此係統的內部設計使用了標準的OO技術,設計了一個鴨子超類(Superclass),並讓各類鴨子繼承此超類。編程
公司需求有新增,主管要求讓讓鴨子能夠飛。編程語言
Joe固然以爲這沒有什麼問題,只須要在Duck類中加入fly()方法就能夠,他也是這麼作的。ide
在產品發佈會上,主管打來電話說,他在發佈會看到不少橡皮鴨子在屏幕飛來飛去……
this
Joe了忽略一件事情,並不是全部的子類均可以擁有fly()方法,好比後來新加入的橡皮鴨(RubberDuck)就不能飛,並且橡皮鴨也不會呱呱叫,只能吱吱叫!在超類中加入新的行爲,會使得某些並不合適該行爲的子類也具備該行爲。對代碼所作的局部修改,影響層面可不僅是局部。編碼
利用繼承的特性
spa
Joy想,既然在父類加入方法行不通,那麼可使用繼承的特性,在子類中覆蓋fly()方法,讓它什麼都不作,同時覆蓋quack()方法,讓它吱吱叫。.net
但,若是之後又加入新的鴨子類型,好比誘餌鴨(DecoyDuck),即不會飛也不會叫……還有不少,咱們能夠本身想獲得。這種設計方式有一下幾種缺點:設計
代碼在多個子類中重複;code
運行時的行爲不容易改變;
很難知道全部鴨子的所有行爲;
改變會牽一髮而動全身,形成其餘鴨子不想要的改變;
利用接口的特性
Joe認識到繼承可能不是答案,Joe知道規格會經常改變,每當有新的鴨子子類出現,他就要被迫檢查並儘量覆蓋fly()方法和quark()方法……這簡直是無窮的噩夢。
因此,他須要一個更清晰的方法,讓「某些」(而不是所有)鴨子類型能夠飛或叫。
他決定把fly()方法從超類中取出來,放進一個Flyable接口中。這麼依賴,只有會飛的鴨子才實現此接口。一樣的方式,也能夠用來設計一個Quackable接口,由於不是全部的鴨子都會叫。
Joe的主管告訴他,這真是一個超笨的主意,這麼一來重複的代碼會變多,若是認爲覆蓋幾個方法就算是差勁,那麼對於48的Duck的子類都要稍微修改一下飛行的行爲,又怎麼說?
若是你的Joe,你要怎麼辦?
咱們知道,並不是「全部」的子類都具備飛行和呱呱叫的行爲,因此繼承並非適當的解決方式。雖然Flyable與Quackable能夠解決「一部分」問題(不會再有會飛的橡皮鴨),但卻形成了代碼沒法複用,這隻能算是從一個噩夢跳進另外一個噩夢。身子,在會飛的鴨子中,飛行的動做可能還有多種變化……
無論你在何處工做,構建些什麼,用何種編程語言,在軟件開發上,一致伴隨你的哪一個不變的真理就是需求變動!
幸運的是,有一個設計原則,剛好適用於此情況。
設計原則
找出應用中可能須要變化之處,把他們獨立出來,不要和哪些不須要變化的代碼混在一塊兒。
換句話說,若是每次新的需求一來,都會使某方面的代碼發生變化,那麼你就能夠肯定,這部分的代碼須要被抽出來,和其餘穩定的代碼有所區分。
也就是說,把會變化的部分取出並封裝起來,以便之後能夠輕易地改動或拓展此部分,而不影響不須要變化的其餘部分。
好,該是把鴨子的行爲從Duck類中取出來的時候了!
咱們知道Duck類內的fly()和quack()會隨着鴨子的不一樣而改變。爲了要把這兩個行爲從Duck類中分開,咱們將把它們從Duck類中取出來,創建一組心累來表明每一個行爲。
如何設計那組實現飛行和呱呱叫的行爲的類呢?
咱們但願一切都有彈性,還向可以「指定」行爲到鴨子的實例。避讓說,產生一個新的綠頭鴨實例,並指定特定類型的飛行行爲給它。乾脆順便讓鴨子的行爲能夠動態地改變好了。換句話說,應該在鴨子類中包涵設定行爲的方法,這樣就能夠在「運行時」動態地「改變」綠頭鴨的飛行行爲。
有了這些目標要實現,接着看看第二個設計原則:
設計原則
針對接口編程,而不是針對實現編程。
利用接口表明每一個行爲,設計兩個接口,FlyBehavior與QuackBehavior,而行爲的每一個實現都將實現其中的一個接口。
這樣的設計,可讓飛行和呱呱叫的動做被其餘的對象複用,由於這些行爲已經與鴨子類無關了。來個天鵝也能夠用。而咱們能夠新增一些行爲,不會影響到既有的行爲類,也不會影響「使用」到飛行行爲的鴨子類。
下面開始編碼,首先建立鴨子類、飛行行爲和呱呱叫行爲。
飛行行爲的接口代碼:
package cn.net.bysoft.strategy; /** * 飛行行爲接口。 * */ public interface FlyBehavior { /** * 飛行動做。 * */ void fly(); }
飛行行爲的實現類,目前有兩個,分別是使用翅膀飛行和不能飛行:
package cn.net.bysoft.strategy; /** * 飛行行爲的實現類。 該類實現飛行行爲,可使用翅膀飛行。 * */ public class FlyWithWings implements FlyBehavior { /** * 可使用翅膀飛行。 * */ public void fly() { System.out.println("使用翅膀飛行"); } }
package cn.net.bysoft.strategy; /** * 飛行行爲的實現類。 該類實現飛行行爲,不能飛行。 * */ public class FlyNoWay implements FlyBehavior { /** * 不能飛行。 * */ public void fly() { System.out.println("不能飛行。"); } }
呱呱叫行爲的接口:
package cn.net.bysoft.strategy; /** * 呱呱叫行爲接口。 * */ public interface QuackBehavior { /** * 呱呱叫動做。 * */ void quack(); }
呱呱叫行爲的實現類,目前有三個,分別是呱呱叫、吱吱叫和不能叫:
package cn.net.bysoft.strategy; /** * 呱呱叫行爲的實現類。 該類實現呱呱叫行爲,能夠呱呱叫。 * */ public class Quack implements QuackBehavior { /** * 呱呱叫。 * */ public void quack() { System.out.println("呱呱叫。"); } }
package cn.net.bysoft.strategy; /** * 呱呱叫行爲的實現類。 該類實現呱呱叫行爲,能夠吱吱叫。 * */ public class Squeak implements QuackBehavior { /** * 吱吱叫。 * */ public void quack() { System.out.println("吱吱叫"); } }
package cn.net.bysoft.strategy; /** * 呱呱叫行爲的實現類。 該類實現呱呱叫行爲,不能叫。 * */ public class QuackNoWay implements QuackBehavior { /** * 不能叫。 * */ public void quack() { System.out.println("不能叫。"); } }
鴨子類的代碼:
package cn.net.bysoft.strategy; /** * 鴨子超類。 * */ /** * @author Administrator * */ public abstract class Duck { /** * 游泳。 * */ public void swim() { System.out.println("在水裏游泳。"); } /** * 調用飛行行爲。 * */ public void performFly() { flyBehavior.fly(); } /** * 調用呱呱叫行爲。 * */ public void performQuack() { quackBehavior.quack(); } /** * 動態設定飛行行爲。 * */ public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } /** * 動態設定呱呱叫行爲。 * */ public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } /** * 顯示外觀。 * */ public abstract void display(); // 飛行行爲。 FlyBehavior flyBehavior; // 呱呱叫行爲。 QuackBehavior quackBehavior; }
package cn.net.bysoft.strategy; /** * 綠頭鴨。 * */ public class DuckOfMallard extends Duck { @Override public void display() { System.out.println("綠頭鴨。"); } }
package cn.net.bysoft.strategy; /** * 玩具鴨。 * */ public class DuckOfToys extends Duck { @Override public void display() { System.out.println("玩具鴨。"); } }
最後是客戶端調用,動態指令行爲:
package cn.net.bysoft.strategy; public class Main { public static void main(String[] args) { // 建立一個玩具鴨。 Duck toysDuck = new DuckOfToys(); // 玩具鴨不會飛。 toysDuck.setFlyBehavior(new FlyNoWay()); // 玩具鴨不會呱呱叫,可是能夠吱吱叫。 toysDuck.setQuackBehavior(new Squeak()); // 玩具鴨開始活動。 toysDuck.display(); toysDuck.performFly(); toysDuck.performQuack(); toysDuck.swim(); System.out.println("\n-------------------------\n"); // 建立一個綠頭鴨。 Duck mallardDuck = new DuckOfMallard(); // 綠頭鴨能夠飛。 mallardDuck.setFlyBehavior(new FlyWithWings()); // 綠頭鴨是真鴨子,能夠呱呱叫。 mallardDuck.setQuackBehavior(new Quack()); // 綠頭鴨開始活動。 mallardDuck.display(); mallardDuck.performFly(); mallardDuck.performQuack(); mallardDuck.swim(); /** * output: * 玩具鴨。 * 不能飛行。 * 吱吱叫 在水裏游泳。 * * ------------------------- * * 綠頭鴨。 * 使用翅膀飛行 呱呱叫。 * 在水裏游泳。 * */ } }
下面是從新設計後的類結構,所指望的一切都有:鴨子繼承Duck類,飛行行爲和呱呱叫行爲能夠在客戶端調用時指派。
請特別注意類之間的關係。關係有兩種,能夠是IS-A(是一個)和HAS-A(有一個)或IMPLEMENTS(實現)。「有一個」可能比「是一個」更好。
這是一個很重要的技巧。也是第三個設計原則:
設計原則
多用組合,少用繼承。
以上就是策略模式的內容。