設(shè)計模式:開篇--體驗設(shè)計

試想一下,如果寫出來的代碼如藝術(shù)品,那將是多么美妙的一件事。因此,小編該好好學(xué)習(xí)一下怎么去設(shè)計代碼了。設(shè)計模式這種東西來到世界上已經(jīng)有相當(dāng)歷史了,可是工作了之后發(fā)現(xiàn)真正能用上的還是少數(shù)人。在寫代碼之前認真思考并設(shè)計一下,能省去相當(dāng)一部分維護代碼的成本。朋友推薦了《Head First 設(shè)計模式 (中文版)》這本書。小編就直接以這本書來打開設(shè)計的大門了。在設(shè)計之前我們體會一下根據(jù)需求的變化,沒有設(shè)計的代碼會讓我們手忙腳亂



1:需求來了-使用繼承

  • 需求:SimUDuck這款游戲會出現(xiàn)各種鴨子,游泳,呱呱叫
  • 考慮:拓展的是多個鴨子,而叫和游泳每個鴨子都有,會有長的不一樣的鴨子
  • 做法:定義鴨子父類Duck,定義三個方法,quack、swim、display。外觀不一樣,那么抽象display。其他實現(xiàn)了。增加鴨子只需要繼承Duck并實現(xiàn)不同的外觀就行了。
public abstract class Duck {

    public void quack() {
        System.out.println("呱呱叫");
    }

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();
}

public class MallardDuck extends Duck {
    @Override
    public void display() {
        System.out.println("外觀:綠頭");
    }
}

public class RedheadDuck extends Duck {

    @Override
    public void display() {
        System.out.println("外觀:紅頭");
    }
}

Demo-1

總結(jié):感覺還是很可以的,感覺以后增加鴨子只要多寫個鴨子類就好了。

2:增加需求-拓展功能

  • 需求:讓鴨子會飛
  • 考慮:難不倒我嘛,只要在Duck實現(xiàn)一個fly的方法下馬所有的鴨子就成功的會飛了
  • 做法:Duck增加fly的方法

代碼僅僅修改了Duck類

public abstract class Duck {

    public void quack() {
        System.out.println("呱呱叫");
    }

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();

    public void fly() {
        System.out.println("飛");
    }
}

Demo-2

總結(jié):內(nèi)心覺得當(dāng)初設(shè)計還是有點用的,完美適應(yīng)增加的需求

3:再增加需求-定制功能的實體

  • 需求:加橡皮鴨和誘餌鴨,橡皮鴨的會吱吱叫,不會飛。誘餌鴨不會叫也不會飛
  • 考慮:父類定義的某些方法的實現(xiàn),這時候字類不需要了,覆蓋掉就好了
  • 做法:繼承Duck增加兩個鴨子RubberDuck和DecoyDuck。覆蓋需要重新實現(xiàn)的方法。
public class RubberDuck extends Duck {


    /**
     * 覆蓋父類方法,實現(xiàn)新的功能
     */
    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }

    @Override
    public void display() {
        System.out.println("外觀:橡皮鴨");
    }

    @Override
    public void fly() {
        //不會飛
    }
}

  public class DecoyDuck extends Duck {
    @Override
    public void display() {
        System.out.println("外觀:誘餌鴨");
    }

    @Override
    public void quack() {
        // 不會叫
    }

    @Override
    public void fly() {
        // 不會飛
    }
}
Demo-3

總結(jié):這時候隨著需求不斷增加,感覺當(dāng)初采用繼承的方式去設(shè)計代碼有點不夠用了

  • 問題:以后所有的鴨子都默認有父類的方法,如果不需要這些方法,就得每個都提供空實現(xiàn),如果忘記了提供,那么就會多出不需要的方法了。代碼不優(yōu)雅,看著都難受。

4、優(yōu)化設(shè)計-使用接口

  • 需求:改變一下設(shè)計,引入接口
  • 考慮:變化的方法有fly和quack。
  • 做法:抽取fly到Flyable接口,抽取quack到Quackable接口,需要的子類自行實現(xiàn)
public interface Flyable {
    void fly();
}
public interface Quackable {
    void quack();
}
public abstract class Duck {

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();

}
public class RedheadDuck extends Duck implements Flyable, Quackable {

    @Override
    public void display() {
        System.out.println("外觀:紅頭");
    }

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

    @Override
    public void quack() {
        System.out.println("呱呱叫");
    }
}
public class MallardDuck extends Duck implements Flyable, Quackable {
    @Override
    public void display() {
        System.out.println("外觀:綠頭");
    }

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

    @Override
    public void quack() {
        System.out.println("呱呱叫");
    }
}

public class RubberDuck extends Duck implements Quackable {

    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }

    @Override
    public void display() {
        System.out.println("外觀:橡皮鴨");
    }
}

public class DecoyDuck extends Duck {
    @Override
    public void display() {
        System.out.println("外觀:誘餌鴨");
    }
}
Demo-4

總結(jié):使用接口之后發(fā)現(xiàn)確實可以不寫這么多空實現(xiàn)了,但是也暴露出了另一個問題,

  • 問題:會出現(xiàn)重復(fù)的代碼,試想一下,如果很多個鴨子都會同樣的飛,那么每個鴨子都要去實現(xiàn)同樣的fly方法。因此簡單的使用接口也不太好。

5:進一步優(yōu)化設(shè)計-使用組合

  • 需求:拓展的時候要解決重復(fù)代碼和動態(tài)實現(xiàn)方法的問題
  • 考慮:繼承會使方法不靈活,接口會導(dǎo)致重復(fù)代碼。嘗試組合一下,并面向接口編程
  • 做法:封裝鴨子的行為(動態(tài)的方法),定義行為接口。定義行為類去實現(xiàn)行為接口(面向接口)。在Duck父類中組合這些行為。而字類提供具體的行為實現(xiàn)。

行為:FlyBehavior以及他的不同實現(xiàn)方式:飛,不會飛...

public interface FlyBehavior {
    void fly();
}
public class FlyNoWay implements FlyBehavior {

    @Override
    public void fly() {
        //不會飛
    }
}
public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("飛");
    }
}

行為:QuackBehavior 以及他的不同實現(xiàn)方式:呱呱叫,吱吱叫,不會叫...

public interface QuackBehavior {
    void quack();
}

public class MuteQuack implements QuackBehavior {
    @Override
    public void quack() {
        //什么都不做,不會叫
    }
}

public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("呱呱叫");
    }
}

public class Squeak implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }
}

實體鴨子:實體鴨子組合行為,字類實例化具體行為

public abstract class Duck {


    FlyBehavior flyBehavior;

    QuackBehavior quackBehavior;

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();

    public void performQuack() {
        quackBehavior.quack();
    }

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

}
public class MallardDuck extends Duck {

    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }

    @Override
    public void display() {
        System.out.println("外觀:綠頭");
    }


}

public class RedheadDuck extends Duck {


    public RedheadDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }

    @Override
    public void display() {
        System.out.println("外觀:紅頭");
    }


}
public class RubberDuck extends Duck {

    public RubberDuck() {
        quackBehavior = new Squeak();
        flyBehavior = new FlyNoWay();
    }

    @Override
    public void display() {
        System.out.println("外觀:橡皮鴨");
    }


}
public class DecoyDuck extends Duck {

    public DecoyDuck() {
        quackBehavior = new MuteQuack();
        flyBehavior = new FlyNoWay();
    }

    @Override
    public void display() {
        System.out.println("外觀:誘餌鴨");
    }

}
Demo-5

總結(jié):光從uml上看就能體會到組合的拓展性是多么的強了。重構(gòu)成這樣之后,不用更擔(dān)心拓展任何的功能的鴨子了,如果沒有功能,你就提供接口,并提供實現(xiàn)類,組合到新的鴨子里即可。那么你可能你會說了,這樣還是不夠動態(tài)。如果我要連行為都是動態(tài)的呢?

6:增加需求-輕松拓展

需求:增加一個模型鴨,不會飛,會呱呱叫。但是模型鴨可以變成吱吱叫,并且會用火箭飛
考慮:由于我們將行為當(dāng)成屬性組合到父類里面了,要改變行為,那么提供改變屬性的set方法即可。
做法:修改Duck類,增加改變屬性flyBehavior、quackBehavior的方法。拓展模型鴨提供默認行為實現(xiàn)。增加fly的實現(xiàn)方式用火箭飛

public class FlyRocketPowered implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("用火箭飛");
    }
}

public abstract class Duck {


    FlyBehavior flyBehavior;

    QuackBehavior quackBehavior;

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();


    public void performQuack() {
        quackBehavior.quack();
    }

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

    /**
     * 修改行為:FlyBehavior
     *
     * @param flyBehavior
     * @return
     */
    public Duck setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
        return this;
    }

    /**
     * 修改行為:QuackBehavior
     *
     * @param quackBehavior
     * @return
     */
    public Duck setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
        return this;
    }
}

public class ModelDuck extends Duck {

    public ModelDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyNoWay();
    }

    @Override
    public void display() {
        System.out.println("外觀:模型鴨");
    }
}

public class Test {
    public static void main(String[] args) {
        Duck modelDuck = new ModelDuck();
        modelDuck.performQuack();
        modelDuck.performFly();

        modelDuck.setQuackBehavior(new Squeak());
        modelDuck.setFlyBehavior(new FlyRocketPowered());

        modelDuck.performFly();
    }
}
Demo-6

總結(jié):從最初的繼承,到接口,到封裝改變,面向接口變成。我們已經(jīng)體會到了設(shè)計的美妙。我們發(fā)現(xiàn)了這樣寫出來的代碼具有較高的拓展性了。無論是拓展行為,還是拓展實體。都可以駕馭的了。但是如果一開始沒有思考和設(shè)計,隨意設(shè)計,一旦項目的代碼和業(yè)務(wù)多起來,那么重構(gòu)的成本將會提高很多,因此寫代碼之前花點時間思考一下拓展性是很有必要的。

總結(jié)一下這個過程當(dāng)中的一些設(shè)計原則。

  • 將變化的部分和固定部分的區(qū)別開來,封裝變化
  • 面向接口編程,而不是面向?qū)崿F(xiàn)編程
  • 組合比繼承好用,多用組合,少用繼承

參考文獻:[1]


  1. 《Head First 設(shè)計模式 (中文版)》 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 設(shè)計模式 開題先說明一下,設(shè)計模式告訴我們?nèi)绾谓M織類和對象以解決某種問題。讓代碼變得更加優(yōu)雅是我們責(zé)無旁貸的任務(wù) ...
    tanghuailong閱讀 491評論 0 2
  • 假如我們現(xiàn)在有一個鴨子,鴨子會呱呱叫,也會游泳,但是每個鴨子的外觀不相同(有白顏色的,有綠色的),那么你會怎么設(shè)計...
    巾二閱讀 349評論 1 1
  • 模擬鴨子游戲的需求 SimUDuck游戲中會出現(xiàn)各種鴨子,一邊游泳戲水,一邊呱呱叫。通過標(biāo)準(zhǔn)的OO技術(shù),設(shè)計一個超...
    一縷陽憶往昔閱讀 556評論 2 0
  • 那是一段令人向往的愛情, 可又伴隨著那么一絲傷感。 因為有太多潮起潮落的瞬間, 所以才有無法平靜的內(nèi)心。 那些感動...
    云之凡M閱讀 390評論 4 2
  • 一個出身西南農(nóng)村的年輕女人,嫁入夫家五年,生下兩個孩子,家庭困難時外出打工補貼家用,勤勤懇懇,任勞任怨,卻遭受丈夫...
    樸生11閱讀 464評論 0 0

友情鏈接更多精彩內(nèi)容