18.java設計模式之中介者模式

xiaokantianse 發表於 2021-01-18

基本需求

  • 智慧家庭包括各種裝置,鬧鐘、咖啡機、電視機、窗簾等
  • 要看電視時,各個裝置可以協同工作,自動完成看電視的準備工作,比如流程為:鬧鈴響起->咖啡機開始做咖啡->窗簾自動落下->電視機開始播放

傳統方案

  • 各個類之間相互呼叫,依賴嚴重,訊息傳遞不準確,呼叫結構混亂
  • 18.java設計模式之中介者模式
  • 說明
    • 當各電器物件有多種狀態改變時,相互之間的呼叫關係會比較複雜
    • 各個 電器物件彼此聯絡,你中有我,我中有你,不利於鬆耦合
    • 各個電器物件之間所傳遞的訊息(引數),容易混亂
    • 當系統增加一個新的電器物件時,或者執行流程改變時,程式碼的可維護性、擴充套件性都不理想 考慮中介者模式

基本介紹

  • 中介者模式(Mediator),用一箇中介物件來封裝一系列的物件互動。中介者使各個物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動

  • 中介者模式屬於行為型模式,使程式碼易於維護

  • 比如MVC模式,C(Controller 控制器)是M(Model 模型)和V(View 檢視)的中介者,在前後端互動時起到了中間人的作用

  • 比如租房子

    • 沒有中介:有六個房東需要出租房屋,適合不一樣的人租住。這六個房東之間有點聯絡。這時租客來租房,看了一號房東的房子不滿意、但是一號房東覺得可以讓他去看看其他五個朋友的房間。然後開始聯絡五個朋友。進行訊息傳遞。一個如此,另外五個亦如此,如果二號房東房屋不出租或者出租出去了,情況發生了變化。他則需要通知其他五個朋友,告訴他們不用再給他介紹租客。這裡就造成了中間一個人發生了變化,需要改動其他五個人
    • 有中介:那六個房東都把房屋交給中介處理。租客A看房間一不滿意直接通過中介看房間二。當房間二被出租了。中介也只需要通知房東二號、然後他們簽訂合同。下次還有人看房間就不會再看房間二。這裡一個出現了變化也就影響改變了一個。並不會影響其他另外五個房東
  • UML類圖(原理)

    • 18.java設計模式之中介者模式
    • 說明
      • Mediator 就是抽象中介者,定義了同事物件到中介者物件的介面
      • Colleague 是抽象同事類
      • ConcreteMediator 具體的中介者物件, 實現抽象方法, 他需要知道所有的具體的同事類,即以一個集合來管理HashMap,並接受某個同事物件訊息,完成相應的任務
      • ConcreteColleague 具體的同事類,會有很多, 每個同事只知道自己的行為,而不瞭解其他同事類的行為(方法),但是他們都依賴中介者物件
  • UML類圖(案例)

    • 18.java設計模式之中介者模式
    • 說明
      • ConcreteMediator是中介者實現類
      • 在建立同事實現類的時候通過其構造方法將其註冊到中介者中,通過sendMessage方法向中介者傳送訊息
      • ConcreteMediator的getMessage方法會接收到同事類的訊息協調呼叫其他物件,是核心方法
  • 程式碼實現

    • public abstract class Mediator {
      
         // 中介者抽象類
      
         // 將同事類加入到中介者中,由中介者對所有同事類進行互動,同事類之間不進行互動
         // 將依賴由網狀變成星狀
         public abstract void register(String colleagueName, Colleague colleague);
      
         // 接收訊息 由同事類發出
         public abstract void getMessage(String colleagueName, int stateChange);
      
         // 傳送訊息,發給同事類,本次案例中沒有用到
         public abstract void sendMessage();
      
      }
      
      // 中介者實現類
      public class ConcreteMediator extends Mediator {
      
         // 使用集合管理所有的中介者,如果key重複,則是最新註冊的 ,可否弄成MapList的形式管理重複key
         private Map<String, Colleague> colleagueMap;
      
         // 名稱的集合,方便我們自己獲取對應的同事類,因為每次註冊時 我們並不知道key,想獲取value時不能獲取,指定名稱 後續使用簡單
         private Map<String, String> nameMap;
      
         public ConcreteMediator() {
             this.colleagueMap = new HashMap<>();
             this.nameMap = new HashMap<>();
         }
      
         @Override
         public void register(String colleagueName, Colleague colleague) {
             colleagueMap.put(colleagueName, colleague);
             if (colleague instanceof Alarm) {
                 // 自定義名稱 方便我們下面使用
                 nameMap.put("alarm", colleagueName);
             } else if (colleague instanceof CoffeeMachine) {
                 nameMap.put("coffeeMachine", colleagueName);
             } else if (colleague instanceof Curtain) {
                 nameMap.put("curtain", colleagueName);
             } else if (colleague instanceof TV) {
                 nameMap.put("tv", colleagueName);
             }
         }
      
         @Override
         public void getMessage(String colleagueName, int stateChange) {
             // 根據得到訊息,完成對應任務,中介者在這個方法,協調各個具體的同事物件,完成任務
             if (colleagueMap.get(colleagueName) instanceof Alarm) {
                 // 處理鬧鐘的訊息 可使用中介者的sendMessage方法進行處理
                 if (stateChange == 0) {
                     ((CoffeeMachine) colleagueMap.get(nameMap.get("coffeeMachine"))).startCoffee();
                     ((TV) colleagueMap.get(nameMap.get("tv"))).startTv();
                 } else if (stateChange == 1) {
                     ((TV) colleagueMap.get(nameMap.get("tv"))).stopTv();
                 }
             } else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
                 // 處理咖啡機的訊息
                 if (stateChange == 0) {
                     ((Curtain) colleagueMap.get(nameMap.get("curtain"))).upCurtains();
                 }
             } else if (colleagueMap.get(colleagueName) instanceof Curtain) {
                 // 處理窗簾的訊息
                 System.out.println();
             } else if (colleagueMap.get(colleagueName) instanceof TV) {
                 // 處理電視機的訊息
                 System.out.println();
             }
         }
      
         @Override
         public void sendMessage() {
      
         }
      }
      
    • public abstract class Colleague {
      
         // 同事抽象類
      
         protected String name;
      
         private Mediator mediator;
      
         public Colleague(String name, Mediator mediator) {
             this.name = name;
             this.mediator = mediator;
         }
      
         public Mediator getMediator() {
             return this.mediator;
         }
      
         // 傳送訊息
         public abstract void sendMessage(int stateChange);
      
      }
      
      // 同事子類一 鬧鐘類
      public class Alarm extends Colleague {
      
         public Alarm(String name, Mediator mediator) {
             super(name, mediator);
             // 將自己在中介者中進行註冊
             mediator.register(name, this);
         }
      
         @Override
         public void sendMessage(int stateChange) {
             // 獲取中介者 呼叫中介者的getMessage方法 將訊息傳遞給中介者 由中介者處理訊息
             this.getMediator().getMessage(super.name, stateChange);
         }
      
         public void sendAlarm(int stateChange) {
             sendMessage(stateChange);
         }
      }
      
      
      // 同事子類二 咖啡機
      public class CoffeeMachine extends Colleague {
      
         public CoffeeMachine(String name, Mediator mediator) {
             super(name, mediator);
             mediator.register(name, this);
         }
      
         @Override
         public void sendMessage(int stateChange) {
             this.getMediator().getMessage(super.name, stateChange);
         }
      
         public void startCoffee() {
             System.out.println("It's time to startcoffee!");
         }
      
         public void finishCoffee() {
             System.out.println("After 5 minutes!");
             System.out.println("Coffee is ok!");
             sendMessage(0);
         }
      }
      
      // 同事子類三 窗簾
      public class Curtain extends Colleague {
      
         public Curtain(String name, Mediator mediator) {
             super(name, mediator);
             mediator.register(name, this);
         }
      
         @Override
         public void sendMessage(int stateChange) {
             this.getMediator().getMessage(super.name, stateChange);
         }
      
         public void upCurtains() {
             System.out.println("I am holding Up Curtains!");
         }
      }
      
      // 同事子類四 電視機
      public class TV extends Colleague {
      
         public TV(String name, Mediator mediator) {
             super(name, mediator);
             mediator.register(name, this);
         }
      
         @Override
         public void sendMessage(int stateChange) {
             this.getMediator().getMessage(super.name, stateChange);
         }
      
         public void startTv() {
             System.out.println("It's time to StartTv!");
         }
      
         public void stopTv() {
             System.out.println("Stop Tv!");
         }
      }
      
      
    • public class Client {
         public static void main(String[] args) {
             // 建立中介者
             Mediator concreteMediator = new ConcreteMediator();
             // 建立同事類 並將其註冊到中介者中
             Alarm alarm = new Alarm("alarm", concreteMediator);
             CoffeeMachine coffeeMachine = new CoffeeMachine("coffeeMachine", concreteMediator);
             Curtain curtain = new Curtain("curtain", concreteMediator);
             // 注意 此處中介者中的nameMap就起到了作用 我們的名稱是TV,而我們中介中使用的tv作為key使用 沒有nameMap就會報錯 nameMap起到橋樑作用
             TV tv = new TV("TV", concreteMediator);
      
             // 使用alarm傳送訊息
             alarm.sendAlarm(0);
             coffeeMachine.finishCoffee();
             alarm.sendAlarm(1);
         }
      }
      

注意事項

  • 多個類相互耦合,會形成網狀結構, 使用中介者模式將網狀結構分離為星型結構,進行解耦
  • 如果增加一個同事類,直接增加即可,降低了類的複雜度,將一對多轉化成了一對一
  • 減少類間依賴,降低了耦合,符合迪米特原則
  • 中介者承擔了較多的責任,一旦中介者出現了問題,整個系統就會受到影響
  • 中介者 承擔了較多的責任,一旦中介者出現了問題,整個系統就會受到影響