1. 程式人生 > >Java中常用的10種設計模式詳解

Java中常用的10種設計模式詳解

1. 觀察者模式

定義了物件之間的一對多的依賴,這樣一來,當一個物件改變時,它的所有的依賴者都會收到通知並自動更新。

  • 對於JDK或者Andorid中都有很多地方實現了觀察者模式,比如XXXView.addXXXListenter , 當然了 XXXView.setOnXXXListener不一定是觀察者模式,因為觀察者模式是一種一對多的關係,對於setXXXListener是1對1的關係,應該叫回調。

	/**
   * 註冊一個觀察者
   */
  public void registerObserver(Observer observer);

  /**
   * 移除一個觀察者
   */
public void removeObserver(Observer observer); /** * 通知所有觀察者 */ public void notifyObservers();
  •  @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(); }
public ObserverUser1(Subject
subject) { subject.registerObserver(this); } @Override public void update(String msg) { Log.e("-----ObserverUser1 ", "得到 3D 號碼:" + msg + ", 我要記下來。 "); }
  •  // 建立服務號
     objectFor3D = new ObjectFor3D();
     // 建立兩個訂閱者
     observerUser1 = new ObserverUser1(objectFor3D);
     observerUser2 = new ObserverUser2(objectFor3D);
     // 兩個觀察者,傳送兩條資訊
     objectFor3D.setMsg("201610121 的3D號為:127");
     objectFor3D.setMsg("20161022 的3D號為:000");

2. 工廠模式

簡單列一下這個模式的家族:

  • 1、靜態工廠模式

    • 這個最常見了,專案中的輔助類,TextUtil.isEmpty等,類+靜態方法。
  • 2、簡單工廠模式(店裡買肉夾饃)

    • 定義:通過專門定義一個類來負責建立其他類的例項,被建立的例項通常都具有共同的父類。
     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:// 預設為酸肉夾饃
                 roujiaMo = new ZSuanRoujiaMo();
                 break;
         }
         return roujiaMo;
     }
  • 3、工廠方法模式(開分店)

    • 定義:定義一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法模式把類例項化的過程推遲到子類。
    • 對比定義:
    • 1、定義了建立物件的一個介面:public abstract RouJiaMo sellRoujiaMo(String type);
    • 2、由子類決定例項化的類,可以看到我們的饃是子類生成的。
  • public abstract RoujiaMo sellRoujiaMo(String type);
  • 4、抽象工廠模式(使用官方提供的原料)

    • 定義:提供一個介面,用於建立相關的或依賴物件的家族,而不需要明確指定具體類。
    • 對比定義:
      • 1、提供一個介面:public interface RouJiaMoYLFactroy
      • 2、用於建立相關的或依賴物件的家族 public Meat createMeat();public YuanLiao createYuanliao();我們介面用於建立一系列的原材料
     /**
     * 準備工作
     */
    public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
        	Meet meet = roujiaMoYLFactory.creatMeet();
        	YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
        	Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉麵-剁肉-完成準備工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
    }

3. 單例設計模式

單例模式主要是為了避免因為建立了多個例項造成資源的浪費,且多個例項由於多次呼叫容易導致結果出現錯誤,而使用單例模式能夠保證整個應用中有且只有一個例項

  • 定義:只需要三步就可以保證物件的唯一性

    • (1) 不允許其他程式用new物件
    • (2) 在該類中建立物件
    • (3) 對外提供一個可以讓其他程式獲取該物件的方法
  • 對比定義:

    • (1) 私有化該類的建構函式
    • (2) 通過new在本類中建立一個本類物件
    • (3) 定義一個公有的方法,將在該類中所建立的物件返回
    private SingletonLanHan() {}
    private static SingletonLanHan singletonLanHanFour;
    public static SingletonLanHan getSingletonLanHanFour() {
   	    if (singletonLanHanFour == null) {
   		synchronized (SingletonLanHan.class) {
   		    if (singletonLanHanFour == null) {
   			singletonLanHanFour = new SingletonLanHan();
   		    }
   		}
   	    }
   	    return singletonLanHanFour;
   }
   

4. 策略模式

策略模式:定義了演算法族,分別封裝起來,讓它們之間可相互替換,此模式讓演算法的變化獨立於使用演算法的客戶。

  • 以建立遊戲角色為例子:
    • 最初的遊戲角色的父類
    • 發現有重複程式碼後,重構後的父類
  • 總結:
    • 1、封裝變化(把可能變化的程式碼封裝起來)
    • 2、多用組合,少用繼承(我們使用組合的方式,為客戶設定了演算法)
    • 3、針對介面程式設計,不針對實現(對於Role類的設計完全的針對角色,和技能的實現沒有關係)
  • 最後測試:建立角色:
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. 介面卡模式

定義:將一個類的介面轉換成客戶期望的另一個介面,介面卡讓原本介面不相容的類可以相互合作。這個定義還好,說介面卡的功能就是把一個介面轉成另一個介面。

  • 以充電器為例項: 手機充電器一般都是5V左右吧,咱天朝的家用交流電壓220V,所以手機充電需要一個介面卡(降壓器)

  • 最後測試:給手機衝個電:

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

6. 命令模式

定義:將“請求”封裝成物件,以便使用不同的請求、佇列或者日誌來引數化其他物件。命令模式也支援可撤銷的操作。(簡化: 將請求封裝成物件,將動作請求者和動作執行者解耦。)

  • 需求:最近智慧家電很火熱,假設現在有電視、電腦、電燈等家電,現在需要你做個遙控器控制所有家電的開關,要求做到每個按鈕對應的功能供使用者個性化,對於新買入家電要有非常強的擴充套件性。
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));// 開門
controlPanel.keyPressed(0);

7. 裝飾者模式

裝飾者模式:若要擴充套件功能,裝飾者提供了比整合更有彈性的替代方案,動態地將責任附加到物件上。

  • 先簡單描述下裝飾者模式發揮作用的地方,當我們設計好了一個類,我們需要給這個類新增一些輔助的功能,並且不希望改變這個類的程式碼,這時候就是裝飾者模式大展雄威的時候了。這裡還體現了一個原則:類應該對擴充套件開放,對修改關閉。

  • 需求:設計遊戲的裝備系統,基本要求,要可以計算出每種裝備在鑲嵌了各種寶石後的攻擊力和描述:

  • 5、最後測試:計算攻擊力和檢視描述:

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

8. 外觀模式

定義:提供一個統一的介面,用來訪問子系統中的一群介面,外觀定義了一個高層的介面,讓子系統更容易使用。其實就是為了方便客戶的使用,把一群操作,封裝成一個方法。

  • 需求:我比較喜歡看電影,於是買了投影儀、電腦、音響、設計了房間的燈光、買了爆米花機,然後我想看電影的時候,我需要一鍵觀影和一鍵關閉。

  • 每個裝置類的開關等操作:

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

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

9. 模板方法模式

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

  • 需求:簡單描述一下:本公司有程式猿、測試、HR、專案經理等人,下面使用模版方法模式,記錄下所有人員的上班情況

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

  • 1、具體方法(Concrete Method)

  • 2、抽象方法(Abstract Method)

  • 3、鉤子方法(Hook Method)

  •  // 具體方法
     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 + "---離開公司");
     }
  •  /**
      * 重寫父類的此方法,使可以檢視離開公司時間
      */
     @Override
     public boolean isNeedPrintDate() {
         return true;
     }
  • 最後測試:

    • 檢視所有人員的工作情況:

       QAWorker qaWorker = new QAWorker("測試人員");
       qaWorker();
       HRWorker hrWorker = new HRWorker("莉莉姐");
       hrWorker.workOneDay();
       ...
    • 檢視程式猿離開公司的時間:

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

10. 狀態模式

定義:允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類。

  • 定義又開始模糊了,理一下,當物件的內部狀態改變時,它的行為跟隨狀態的改變而改變了,看起來好像重新初始化了一個類似的。

  • 需求:已自動售貨機為例(有已投幣、未投幣等狀態和投幣、投幣等方法)

  • // 放錢
    public void insertMoney() {
        currentState.insertMoney();
    }
    // 退錢
    public void backMoney() {
        currentState.backMoney();
    }
    // 轉動曲柄
    public void turnCrank() {
        currentState.turnCrank();
        if (currentState == soldState || currentState == winnerState) {
            currentState.dispense();//兩種情況會出貨
        }
    }
    // 出商品
    public void dispense() {
        Log.e("VendingMachineBetter", "---發出一件商品");
        if (count > 0) {
            count--;
        }
    }
    // 設定對應狀態
    public void setState(State state) {
        this.currentState = state;
    }
  • 改進後的售貨機測試:

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