java基礎_設計模式_設計基礎(小鴨子游戲)

小鴨子游戲,是好多愛好者接觸設計模式、認知設計模式概念的一個入門。
每個初學者的理解不同,我加上自己的理解大體是這樣的:前提是處理大規模時,假設池塘中有10000頭小鴨子,有紅頭鴨,野鴨子,木頭鴨子等等,會叫會游泳,肯定是定義一個父類Duck,擁有這樣的基本行爲屬性;現在要求鴨子中400頭,可以飛,該怎麼做?
有句話叫:當你處理大規模時,一定要寫抽象層次編程

問題:400只小鴨子可以飛,怎麼做?

1.最直接想到的就是 父類中定義fly()方法,全部的子鴨子去重寫,要飛的就子類自己實現飛這個行爲,不要飛的子類可以空實現。目的實現了,存在的問題:①全部子類都要改代碼  ②實現的fly方法 具體實現無論是不是空,都要在棧中保留方法的引用 而維護方法的指針需要成本尤其是大量時。  基於這兩點肯定不合適。

總結:通過繼承實現在父類中聲明的行爲,主要有以下缺點:

the followings are the disadvantages of using inheritance to provide Duck behavior

①Code is duplicated across subclasses (代碼在多個子類中重複)

② Runtime behavior changes are difficult (運行時行爲不易改變)

③Changes can unintentionally affect other ducks (改變會牽一髮而動全身,造成部分子類型不想要的改變)


2.定義一個FlyAble接口,然後定義400只需要飛的小鴨子實現這個接口,然後實現自己的方法,有沒有發現問題的所在,需要每一個子類都去實現這個接口,然後寫一樣的fly代碼;代碼如下:

public interface Flyable {
    void fly();
}
public class RedHeadDuck extends Duck implements Flyable {
    @Override
    void display() {
        System.out.println("紅頭的");
    }

    @Override
    public void fly() {
        System.out.println("要飛的");
    }
}

圖示:



這就是“java的接口不具有實現代碼,實現接口無法達到代碼的複用”,也就意味着“當你需要修改某個行爲,你需要往下追蹤,並修改每一個定義了此行爲的類。” 這兩句話,可以細細體會,就是接口不具有代碼的實現,無法達到代碼的複用。

實現Flyable接口的方式到底有什麼壞處?
假設400個鴨子要能飛,那要400個鴨子實現這個接口,每個都重寫400次fly方法,然後定義飛的行爲,要是不同的鴨子有了高高飛,擦地飛,旋轉着飛,是不是,每個鴨子要寫不同的飛方法,比如

a   =》高高飛

b   =》擦地飛
c   =》旋轉着飛

現在d e f g 也要旋轉飛 那肯定要每一個都寫旋轉飛的方法  這屬於“接口不具有實現代碼,所以實現接口無法達到代碼的複用”。


如果有專門的高高飛行爲類,擦地飛行爲類,旋轉着飛行爲類,都去實現flybehavior行爲接口,重寫fly表達行爲,那每次用的時候,就不用寫這麼多重複的代碼了。 而且當你想修改方法的時候只需要改一處就好了。

可以試想,將變化的內容定義形成接口可以實現變化內容和不變內容的剝離。 其接口的實現類可以實現變化內容的重用,即理解爲

這些實現類並非Duck.java的子類型,而是專門的一組實現類,稱之爲 行爲類。由行爲類而不是Duck.java的子類型實現接口。這樣才能保證變化的行爲獨立於不變的內容。

代碼如下:

public interface FlyBehavior {
    void fly();
}

//具體的行爲類 用翅膀飛

public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("==帶翅膀飛");
    }
}



/**
 * 父類
 */
public abstract class Duck {

    FlyBehavior flyBehavior;
   
    String color;
    int age;

    public void performFly() {
        flyBehavior.fly();
    }

    void performSwim() {
        System.out.println("==所有的鴨子都會游泳呢");
    }
}

/**
 * 紅頭鴨
 */

public class RedheadDuck extends Duck {
    public RedheadDuck() {
        flyBehavior = new FlyWithWings();
    }
}


測試類:

/**
 * 測試類
 */

public class TestDuck {
    public static void main(String[] args) {
        System.out.println("=============紅頭鴨子=========");
        showRedHeadDuck();
    }

     private static void showRedHeadDuck() {
        Duck duck = new RedheadDuck();
        duck.performFly();
      }
}
 這樣就實現了鴨子類和fly這一行爲的分離,兩着沒有了關係。

以前的做法是:行爲是繼承自Duck超類的具體實現而來,或者實現某個接口並由子類自行實現而來。 這兩種方法都是依賴於實現。我們被實現綁的死死的,沒有辦法更改行爲(除非寫更多的代碼)

這種方法和繼承的不同之處在於,小鴨子的行爲不是繼承而來,而是和適當的行爲對象結合。這是一個很重要的技巧,其實使用了策略模式中的第三個設計原則,多用組合 少用繼承。

當你處理大規模時 一定要寫抽象層次編程。

整個過程就是從is-a轉成has-a。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章