java 常用十種設(shè)計(jì)模式示例歸納 | 已打包請(qǐng)帶走

設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。

GitHub地址

DesignPattern

文章說(shuō)明

一個(gè)Demo,集合常用的十種設(shè)計(jì)模式,每個(gè)模式使用易被人們接受的案例講述,按模式分包,使用設(shè)計(jì)模式前后對(duì)比,界面顯示定義講解,讓你更深刻的了解每種設(shè)計(jì)模式。
大部分案例來(lái)自張鴻洋的博客。如有錯(cuò)誤歡迎指正,如有侵權(quán),請(qǐng)聯(lián)系我刪除。

項(xiàng)目結(jié)構(gòu)

包結(jié)構(gòu).png

設(shè)計(jì)模式分為三種類型,共23種:

MainActivity.png

博客目錄

對(duì)應(yīng)模式所在的包

模式分析

1. 觀察者模式

定義了對(duì)象之間的一對(duì)多的依賴,這樣一來(lái),當(dāng)一個(gè)對(duì)象改變時(shí),它的所有的依賴者都會(huì)收到通知并自動(dòng)更新。

  • 對(duì)于JDK或者Andorid中都有很多地方實(shí)現(xiàn)了觀察者模式,比如XXXView.addXXXListenter , 當(dāng)然了 XXXView.setOnXXXListener不一定是觀察者模式,因?yàn)橛^察者模式是一種一對(duì)多的關(guān)系,對(duì)于setXXXListener是1對(duì)1的關(guān)系,應(yīng)該叫回調(diào)。

  • 專題接口:Subject.java ;

    /**
     * 注冊(cè)一個(gè)觀察者
     */
   public void registerObserver(Observer observer);
   /**
    * 移除一個(gè)觀察者
    */
   public void removeObserver(Observer observer);
   /**
    * 通知所有觀察者
    */
   public void notifyObservers();
  • 3D服務(wù)號(hào)的實(shí)現(xiàn)類:ObjectFor3D.java

     @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    @Override
    public void removeObserver(Observer observer) {
        int index = observers.indexOf(observer);
        if (index >= 0) {
            observers.remove(index);
        }
    }
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(msg);
        }
    }
    /**
     * 主題更新信息
     */
    public void setMsg(String msg) {
        this.msg = msg;
        notifyObservers();
    }
    
  • 所有觀察者需要實(shí)現(xiàn)此接口:Observer.java

public ObserverUser1(Subject subject) {
       subject.registerObserver(this);
   }
   @Override
   public void update(String msg) {
       Log.e("-----ObserverUser1 ", "得到 3D 號(hào)碼:" + msg + ", 我要記下來(lái)。 ");
   }
  • 最后測(cè)試:ObserverActivity.java

    // 創(chuàng)建服務(wù)號(hào)
    objectFor3D = new ObjectFor3D();
    // 創(chuàng)建兩個(gè)訂閱者
    observerUser1 = new ObserverUser1(objectFor3D);
    observerUser2 = new ObserverUser2(objectFor3D);
    // 兩個(gè)觀察者,發(fā)送兩條信息
    objectFor3D.setMsg("201610121 的3D號(hào)為:127");
    objectFor3D.setMsg("20161022 的3D號(hào)為:000");
    

2. 工廠模式

簡(jiǎn)單列一下這個(gè)模式的家族:

  • 1、靜態(tài)工廠模式

    • 這個(gè)最常見(jiàn)了,項(xiàng)目中的輔助類,TextUtil.isEmpty等,類+靜態(tài)方法。
  • 2、簡(jiǎn)單工廠模式(店里買肉夾饃)

    • 定義:通過(guò)專門定義一個(gè)類來(lái)負(fù)責(zé)創(chuàng)建其他類的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類。
    • 根據(jù)類型直接創(chuàng)建肉夾饃:SimpleRoujiaMoFactory.java
    public RoujiaMo creatRoujiaMo(String type) {
        RoujiaMo roujiaMo = null;
        switch (type) {
            case "Suan":
                roujiaMo = new ZSuanRoujiaMo();
                break;
            case "La":
                roujiaMo = new ZLaRoujiaMo();
                break;
            case "Tian":
                roujiaMo = new ZTianRoujiaMo();
                break;
            default:// 默認(rèn)為酸肉夾饃
                roujiaMo = new ZSuanRoujiaMo();
                break;
        }
        return roujiaMo;
    }
    
  • 3、工廠方法模式(開(kāi)分店)

    • 定義:定義一個(gè)創(chuàng)建對(duì)象的接口,但由子類決定要實(shí)例化的類是哪一個(gè)。工廠方法模式把類實(shí)例化的過(guò)程推遲到子類。
    • 對(duì)比定義:
    • 1、定義了創(chuàng)建對(duì)象的一個(gè)接口:public abstract RouJiaMo sellRoujiaMo(String type);
    • 2、由子類決定實(shí)例化的類,可以看到我們的饃是子類生成的。
  • 提供創(chuàng)建肉夾饃店抽象方法:RoujiaMoStore.java

    public abstract RoujiaMo sellRoujiaMo(String type);
    
  • 具體實(shí)現(xiàn)抽象方法:XianRoujiaMoStore.java

  • 分店依舊使用簡(jiǎn)單工廠模式:XianSimpleRoujiaMoFactory.java

  • 4、抽象工廠模式(使用官方提供的原料)

    • 定義:提供一個(gè)接口,用于創(chuàng)建相關(guān)的或依賴對(duì)象的家族,而不需要明確指定具體類。
    • 對(duì)比定義:
      • 1、提供一個(gè)接口:public interface RouJiaMoYLFactroy
      • 2、用于創(chuàng)建相關(guān)的或依賴對(duì)象的家族 public Meat createMeat();public YuanLiao createYuanliao();我們接口用于創(chuàng)建一系列的原材料。
    • 創(chuàng)建用于提供原料的接口工廠:RoujiaMoYLFactory.java
    • 各自分店實(shí)現(xiàn)接口,完成原料提供:XianRoujiaMoYLFoctory.java
    • 準(zhǔn)備時(shí),使用官方的原料:RoujiaMo.java
    /**
       * 準(zhǔn)備工作
       */
    public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
           Meet meet = roujiaMoYLFactory.creatMeet();
           YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
           Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成準(zhǔn)備工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
    }
    

3. 單例設(shè)計(jì)模式

單例模式主要是為了避免因?yàn)閯?chuàng)建了多個(gè)實(shí)例造成資源的浪費(fèi),且多個(gè)實(shí)例由于多次調(diào)用容易導(dǎo)致結(jié)果出現(xiàn)錯(cuò)誤,而使用單例模式能夠保證整個(gè)應(yīng)用中有且只有一個(gè)實(shí)例

  • 定義:只需要三步就可以保證對(duì)象的唯一性

    • (1) 不允許其他程序用new對(duì)象
    • (2) 在該類中創(chuàng)建對(duì)象
    • (3) 對(duì)外提供一個(gè)可以讓其他程序獲取該對(duì)象的方法
  • 對(duì)比定義:

  • (1) 私有化該類的構(gòu)造函數(shù)

  • (2) 通過(guò)new在本類中創(chuàng)建一個(gè)本類對(duì)象

  • (3) 定義一個(gè)公有的方法,將在該類中所創(chuàng)建的對(duì)象返回

  • 餓漢式[可用]:SingletonEHan.java

  • 含懶漢式[雙重校驗(yàn)鎖 推薦用]:SingletonLanHan.java

private SingletonLanHan() {}
private static SingletonLanHan singletonLanHanFour;
public static SingletonLanHan getSingletonLanHanFour() {
if (singletonLanHanFour == null) {
synchronized (SingletonLanHan.class) {
if (singletonLanHanFour == null) {
singletonLanHanFour = new SingletonLanHan();
}
}
}
return singletonLanHanFour;
}


- 內(nèi)部類[推薦用]:[SingletonIn.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/singleton/inclass/SingletonIn.java)
- 枚舉[推薦用]:[SingletonEnum.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/singleton/enums/SingletonEnum.java)

![單例設(shè)計(jì)模式.png](http://upload-images.jianshu.io/upload_images/1354448-756908a3ca147593.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/240)


###4. 策略模式
> 策略模式:定義了算法族,分別封裝起來(lái),讓它們之間可相互替換,此模式讓算法的變化獨(dú)立于使用算法的客戶。

- 以創(chuàng)建游戲角色為例子:
    - 最初的游戲角色的父類:[Role.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/strategy/old/Role.java)
    - 發(fā)現(xiàn)有重復(fù)代碼后,重構(gòu)后的父類:[Role.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/strategy/better/Role.java)
- 總結(jié):
    - 1、封裝變化(把可能變化的代碼封裝起來(lái))
    - 2、多用組合,少用繼承(我們使用組合的方式,為客戶設(shè)置了算法)
    - 3、針對(duì)接口編程,不針對(duì)實(shí)現(xiàn)(對(duì)于Role類的設(shè)計(jì)完全的針對(duì)角色,和技能的實(shí)現(xiàn)沒(méi)有關(guān)系)
- 最后測(cè)試:創(chuàng)建角色:

```java
RoleA roleA = new RoleA("---A");
roleA.setiDisplayBehavior(new DisplayYZ())
      .setiAttackBehavior(new AttackXL())
      .setiDefendBehavior(new DefendTMS())
      .setiRunBehavior(new RunJCTQ());
roleA.display();// 樣子
roleA.attack();// 攻擊
roleA.run();// 逃跑
roleA.defend();// 防御

5. 適配器模式

定義:將一個(gè)類的接口轉(zhuǎn)換成客戶期望的另一個(gè)接口,適配器讓原本接口不兼容的類可以相互合作。這個(gè)定義還好,說(shuō)適配器的功能就是把一個(gè)接口轉(zhuǎn)成另一個(gè)接口。

  • 以充電器為實(shí)例: 手機(jī)充電器一般都是5V左右吧,咱天朝的家用交流電壓220V,所以手機(jī)充電需要一個(gè)適配器(降壓器)

  • 一部手機(jī): Mobile.java

  • 手機(jī)依賴一個(gè)提供5V電壓的接口: V5Power.java

  • 我們擁有的是220V家用交流電: V220Power.java

  • 適配器,完成220V轉(zhuǎn)5V的作用V5PowerAdapter.java

  • 最后測(cè)試:給手機(jī)沖個(gè)電:

    Mobile mobile = new Mobile();
    V5Power v5Power = new V5PowerAdapter(new V200Power());
    mobile.inputPower(v5Power);
    

6. 命令模式

定義:將“請(qǐng)求”封裝成對(duì)象,以便使用不同的請(qǐng)求、隊(duì)列或者日志來(lái)參數(shù)化其他對(duì)象。命令模式也支持可撤銷的操作。(簡(jiǎn)化: 將請(qǐng)求封裝成對(duì)象,將動(dòng)作請(qǐng)求者和動(dòng)作執(zhí)行者解耦。)

  • 需求:最近智能家電很火熱,假設(shè)現(xiàn)在有電視、電腦、電燈等家電,現(xiàn)在需要你做個(gè)遙控器控制所有家電的開(kāi)關(guān),要求做到每個(gè)按鈕對(duì)應(yīng)的功能供用戶個(gè)性化,對(duì)于新買入家電要有非常強(qiáng)的擴(kuò)展性。
  • 1、家電的API:Door.java
  • 2、把命令封裝成類:
  • 3、遙控器:ControlPanel.java
  • 4、定義一個(gè)命令,可以干一系列的事情:QuickCommand.java
QuickCommand quickCloseCommand = new QuickCommand(new Command[]{new LightOffCommand(light), new ComputerOffCommand(computer), new DoorCloseCommand(door)});
controlPanel.setCommands(6, quickOpenCommand);
controlPanel.keyPressed(6);
controlPanel.setCommands(0, new DoorOpenCommand(door));// 開(kāi)門
controlPanel.keyPressed(0);
命令模式

7. 裝飾者模式

裝飾者模式:若要擴(kuò)展功能,裝飾者提供了比集成更有彈性的替代方案,動(dòng)態(tài)地將責(zé)任附加到對(duì)象上。

  • 先簡(jiǎn)單描述下裝飾者模式發(fā)揮作用的地方,當(dāng)我們?cè)O(shè)計(jì)好了一個(gè)類,我們需要給這個(gè)類添加一些輔助的功能,并且不希望改變這個(gè)類的代碼,這時(shí)候就是裝飾者模式大展雄威的時(shí)候了。這里還體現(xiàn)了一個(gè)原則:類應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。

  • 需求:設(shè)計(jì)游戲的裝備系統(tǒng),基本要求,要可以計(jì)算出每種裝備在鑲嵌了各種寶石后的攻擊力和描述:

  • 1、裝備的超類:IEquip.java

  • 2、各個(gè)裝備的實(shí)現(xiàn)類:

  • 3、裝飾品的超類(裝飾品也屬于裝備):IEquipDecorator.java

  • 4、裝飾品的實(shí)現(xiàn)類:

  • 5、最后測(cè)試:計(jì)算攻擊力和查看描述:

    Log.e("---", "一個(gè)鑲嵌2顆紅寶石,1顆藍(lán)寶石的靴子: ");
    IEquip iEquip = new RedGemDecotator(new RedGemDecotator(new BlueGemDecotator(new ShoeEquip())));
    Log.e("---", "攻擊力:" + iEquip.caculateAttack());
    Log.e("---", "描述語(yǔ):" + iEquip.description());
    

8. 外觀模式

定義:提供一個(gè)統(tǒng)一的接口,用來(lái)訪問(wèn)子系統(tǒng)中的一群接口,外觀定義了一個(gè)高層的接口,讓子系統(tǒng)更容易使用。其實(shí)就是為了方便客戶的使用,把一群操作,封裝成一個(gè)方法。

  • 需求:我比較喜歡看電影,于是買了投影儀、電腦、音響、設(shè)計(jì)了房間的燈光、買了爆米花機(jī),然后我想看電影的時(shí)候,我需要一鍵觀影和一鍵關(guān)閉。

  • 每個(gè)設(shè)備類的開(kāi)關(guān)等操作:

  • eg: 爆米花機(jī):PopcornPopper.java

  • 電影院類:HomeTheaterFacade.java

    /**
     * 一鍵觀影
     */
    public void watchMovie() {
        computer.on();
        light.down();
        popcornPopper.on();
        popcornPopper.makePopcorn();
        projector.on();
        projector.open();
        player.on();
        player.make3DListener();
    }
    
  • 最后測(cè)試:一鍵觀影:

    new HomeTheaterFacade(computer, light, player, popcornPopper, projector).watchMovie();
    

9. 模板方法模式

定義:定義了一個(gè)算法的骨架,而將一些步驟延遲到子類中,模版方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法的步驟。

  • 需求:簡(jiǎn)單描述一下:本公司有程序猿、測(cè)試、HR、項(xiàng)目經(jīng)理等人,下面使用模版方法模式,記錄下所有人員的上班情況

  • 模板方法模式中的三類角色

  • 1、具體方法(Concrete Method)

  • 2、抽象方法(Abstract Method)

  • 3、鉤子方法(Hook Method)

  • 工人的超類:Worker.java

    // 具體方法
    public final void workOneDay() {
        Log.e("workOneDay", "-----------------work start----------------");
        enterCompany();
        work();
        exitCompany();
        Log.e("workOneDay", "-----------------work end----------------");
    }
    // 工作  抽象方法
    public abstract void work();
    // 鉤子方法
    public boolean isNeedPrintDate() {
        return false;
    }
    private void exitCompany() {
        if (isNeedPrintDate()) {
            Log.e("exitCompany", "---" + new Date().toLocaleString() + "--->");
        }
        Log.e("exitCompany", name + "---離開(kāi)公司");
    }
    
  • 程序員實(shí)現(xiàn)類(可得知時(shí)間):ITWorker.java

    /**
     * 重寫(xiě)父類的此方法,使可以查看離開(kāi)公司時(shí)間
     */
    @Override
    public boolean isNeedPrintDate() {
        return true;
    }
    
  • 最后測(cè)試:

    • 查看所有人員的工作情況:

      QAWorker qaWorker = new QAWorker("測(cè)試人員");
      qaWorker();
      HRWorker hrWorker = new HRWorker("莉莉姐");
      hrWorker.workOneDay();
      ...
      
    • 查看程序猿離開(kāi)公司的時(shí)間:

      ITWorker itWorker = new ITWorker("jingbin");
      itWorker.workOneDay();
      

10. 狀態(tài)模式

定義:允許對(duì)象在內(nèi)部狀態(tài)改變時(shí)改變它的行為,對(duì)象看起來(lái)好像修改了它的類。

  • 定義又開(kāi)始模糊了,理一下,當(dāng)對(duì)象的內(nèi)部狀態(tài)改變時(shí),它的行為跟隨狀態(tài)的改變而改變了,看起來(lái)好像重新初始化了一個(gè)類似的。

  • 需求:已自動(dòng)售貨機(jī)為例(有已投幣、未投幣等狀態(tài)和投幣、投幣等方法)

  • 最初實(shí)現(xiàn)待改進(jìn)的售貨機(jī):VendingMachine.java

  • 改進(jìn)后的售貨機(jī)(更具有延展性):VendingMachineBetter.java

    // 放錢
    public void insertMoney() {
        currentState.insertMoney();
    }
    // 退錢
    public void backMoney() {
        currentState.backMoney();
    }
    // 轉(zhuǎn)動(dòng)曲柄
    public void turnCrank() {
        currentState.turnCrank();
        if (currentState == soldState || currentState == winnerState) {
            currentState.dispense();//兩種情況會(huì)出貨
        }
    }
    // 出商品
    public void dispense() {
        Log.e("VendingMachineBetter", "---發(fā)出一件商品");
        if (count > 0) {
            count--;
        }
    }
    // 設(shè)置對(duì)應(yīng)狀態(tài)
    public void setState(State state) {
        this.currentState = state;
    }
    
  • 狀態(tài)的接口:State.java

  • 對(duì)應(yīng)狀態(tài)的接口實(shí)現(xiàn)類:

  • 改進(jìn)后的售貨機(jī)測(cè)試:

    // 初始化售貨機(jī),且里面有3個(gè)商品
    VendingMachineBetter machineBetter = new VendingMachineBetter(3);
    machineBetter.insertMoney();
    machineBetter.turnCrank();
    

參考鏈接

Thanks

End

感興趣的小伙伴可以Star哦~

GitHub:DesignPattern

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,993評(píng)論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,779評(píng)論 18 399
  • 面向?qū)ο蟮牧笤瓌t 單一職責(zé)原則 所謂職責(zé)是指類變化的原因。如果一個(gè)類有多于一個(gè)的動(dòng)機(jī)被改變,那么這個(gè)類就具有多于...
    JxMY閱讀 975評(píng)論 1 3
  • 助學(xué)貸款是國(guó)家對(duì)普通高校貧困家庭學(xué)生資助所采取的一項(xiàng)重大措施,對(duì)于廣大寒門學(xué)子是一項(xiàng)非常好的政策。 由于在校學(xué)習(xí)期...
    玩頭條的小伙子閱讀 5,938評(píng)論 7 10
  • 首先導(dǎo)入兩張圖片 圖片一 圖片二 接著復(fù)制圖片一的背景圖層到圖片二的位置。 提示,紅色圈內(nèi)可以選擇復(fù)制到哪里! 之...
    奧哈閱讀 451評(píng)論 0 5