繼承不能很好的解決問題,因為鴨子的行為在子類中會不斷的變化,為了適應這種變化,我們不得不一遍遍的在子類中覆蓋父類的方法。
介面也不能解決這個問題,因為介面的代碼布不具有複用的特性,如果某一天所有的鴨子都變成嘰嘰叫,那麼,你不得不到所有的子類中一個個的修改。
幸運的是,有一個設計原則適應這種情況:
找出應用可能需要變化之處,把他們獨立出來,不要和那些不需要變化的代碼混淆在一起。把會變化的部分取出來並封裝起來,以便以後可以輕易的改動或者擴充此部分,而不需要影響其他不需要變化的部分。
一、首先,分清楚該應用中變化的和不變化的部分,將他們區分開。然後,將變化的部分fly和quack建立兩組類,一組與fly有關,一組和quack有關,每一組實現自己各自的動作。
飛行行為的介面:FlyBehavior.java,具體的飛行的行為都要實現該介面
/** * 具體的飛行的行為都要實現該介面 */public interface FlyBehavior {void fly();}
用翅膀飛行:FlyWithWings.java
/** * 用翅膀飛行的行為 */public class FlyWithWings implements FlyBehavior{@Overridepublic void fly() {System.out.println("我用翅膀飛行");}}
不能飛行:FlyNoWay.java
/** * 不能飛行 */public class FlyNoWay implements FlyBehavior{@Overridepublic void fly() {//不能飛行,空實現}}
叫 行為的介面:QuackBehavior.java,具體的叫的行為都要實現該介面
/** * 具體的叫的行為都要實現該介面 */public interface QuackBehavior {void quack();}
呱呱叫:Quack.java
/** * 呱呱叫 */public class Quack implements QuackBehavior{@Overridepublic void quack() {System.out.println("我可以呱呱叫");}}
吱吱叫:Squeak.java
/** * 吱吱叫 */public class Squeak implements QuackBehavior{@Overridepublic void quack() {System.out.println("我可以吱吱叫");}}
不會叫:MuteQuack.java
/** * 不會叫 */public class Quack implements QuackBehavior{@Overridepublic void quack() {//空實現}}
這樣就設計好了行為的變化,這樣的設計,可以使飛行和呱呱叫的動作被其他的對象複用,因為這些行為已經和鴨子沒有關係。任何具備這兩種特性的對象都可以利用。
並且,我們可以新增加一些行為,而不會對其他的行為有任何的影響。
二、鴨子的行為都已經封裝好了,現在就是要把它們整合到鴨子中來。
1、鴨子的超類:Duck.java
/** * 所有鴨子的父類 */public class Duck {FlyBehavior flyBehavior;//飛行行為QuackBehavior quackBehavior;//叫的行為public void swim() {System.out.println("我可以遊泳");}public void display() {}public void performFly() {flyBehavior.fly();}public void performQuack() {quackBehavior.quack();}}
2、鴨子的子類
綠頭鴨:
/** * 綠頭鴨,會飛,會叫 */public class MallardDuck extends Duck {public MallardDuck() {flyBehavior = new FlyWithWings();quackBehavior = new Quack();}@Overridepublic void display() {System.out.println("我是綠頭鴨!");}}
紅頭鴨:
/** * 紅頭鴨,會飛,會叫 */public class ReadheadDuck extends Duck {public ReadheadDuck() {flyBehavior=new FlyWithWings();quackBehavior=new Quack();}@Overridepublic void display() {System.out.println("我是紅頭鴨!");}}
橡皮鴨:
/** * 橡皮鴨是吱吱叫的,不會飛的, */public class RubberDuck extends Duck{public RubberDuck() {flyBehavior=new FlyNoWay();quackBehavior=new Squeak();}@Overridepublic void display() {System.out.println("我是橡皮鴨!");}}
誘餌鴨:
/** * 誘餌鴨,不會飛,不會叫 */public class DecoyDuck extends Duck{//針對不同的鴨子給與不同的實現public DecoyDuck() {flyBehavior=new FlyNoWay();quackBehavior=new MuteQuack();}@Overridepublic void display() {System.out.println("我是誘餌鴨!");}}
3、測試鴨子的行為
public class TestClass {public static void main(String[] args) {MallardDuck mallardDuck=new MallardDuck();//綠頭鴨ReadheadDuck readheadDuck=new ReadheadDuck();//紅頭鴨RubberDuck rubberDuck=new RubberDuck();//橡皮鴨DecoyDuck decoyDuck=new DecoyDuck();//誘餌鴨mallardDuck.display();mallardDuck.performFly();mallardDuck.swim();mallardDuck.performQuack();System.out.println("-------------------");readheadDuck.display();readheadDuck.performFly();readheadDuck.swim();readheadDuck.performQuack();System.out.println("-------------------");rubberDuck.display();rubberDuck.performFly();rubberDuck.performQuack();rubberDuck.swim();System.out.println("-------------------");decoyDuck.display();decoyDuck.performFly();decoyDuck.swim();decoyDuck.performQuack();}}
4、測試結果:
我是綠頭鴨!我用翅膀飛行我可以遊泳我會呱呱叫-------------------我是紅頭鴨!我用翅膀飛行我可以遊泳我會呱呱叫-------------------我是橡皮鴨!我可以吱吱叫我可以遊泳-------------------我是誘餌鴨!我可以遊泳
三、這樣的一個鴨子應用的體系就已經設計好了,不管是我們要添加更多的飛行行為,還是叫的行為,直接繼承FlyBehaviour介面或者QuackBehavior介面,而不會對其他的部分產生影響。修改某個鴨子的行為,也只要在其構造的方法裡面針對不同行為的實現就可以了。
而且我們還可以在啟動並執行時候動態修改鴨子的行為。只需要在Duck超類中添加兩個方法就可以了
public void setFlyBehavior(FlyBehavior flyBehavior){this.flyBehavior=flyBehavior;}public void setFlyBehavior(QuackBehavior quackBehavior){this.quackBehavior=quackBehavior;}