中介者模式 調停者 Mediator 行爲型 設計模式(二十一)

  用一箇中介對象(中介者)來封裝一系列的對象交互,中介者使各對象不須要顯式地相互引用,從而使其耦合鬆散
  
  並且能夠獨立地改變它們之間的交互。
  
  中介者模式又稱爲調停者模式。
  
  面向對象的程序設計中,咱們一般將功能進行分解,按照職責以類爲維度進行劃分,也就是使用時功能最終將分佈在多個對象中
  
  而且咱們會盡量的保持對象功能的單一(單一職責原則)
  
  相對於對象的單一職責來講,任何的系統或者模塊的功能卻並不會單一,每每都是有多個對象交互協做來實現全部的功能
  
  對象之間不可避免的須要創建鏈接
  
  換句話說
  
  系統(或者某模塊)必然是複雜的(沒有什麼系統能夠幾個對象就輕鬆搞定,那樣或許也不能稱之爲系統了吧)
  
  功能必然會分佈在多個對象中
  
  多個對象協做必然須要聯繫,這必然致使耦合的產生
  
  image_5c171b2a_724a
  
  如上圖所示,雖然系統對外呈現是一個統一的總體,可是,內部各個模塊之間極可能是緊密的耦合
  
  各個模塊相互聯繫,可能互相持有引用,會出現網狀結構,徹底不符合迪米特法則。
  
  若是對系統進行改動,將會變得困難。
  
  咱們以裝修爲例
  
  通常裝修公司都會給每個項目配備一個項目經理(這個項目也就是你家這個單子了,項目經理就是包工頭)
  
  裝修的通常階段分爲:前期設計→拆改→水電→瓦工→木工→油漆→安裝→保潔→軟裝
  
  項目經理手上常常同時有幾個工地在同步進行,只要錯的開就行了
  
  由於每一個階段都是有前後順序的,你不可能先木工,而後再去拆改;
  
  由於每一個階段也都須要必定時間,也意味着這一撥人不可能同時在你家工做
  
  開工後項目經理會進行工做安排
  
  水電工結束了A以後,項目經理會安排他到B,而後安排瓦工到A,而後........
  
  全部的順序都是由項目經理負責調度,水電工能夠徹底不認識瓦工,他們也徹底不須要進行聯繫
  
  有事兒找項目經理
  
  若是沒有項目經理,會是什麼場景?
  
  那就是人人都是項目經理,人人都須要管本身,還須要管別人
  
  也就是每一個人安排分配本身的時間與任務
  
  水電工結束後須要聯繫瓦工進場,若是瓦工發現有遺留問題,須要聯繫水電工進行溝通
  
  木工須要聯繫瓦工確認進展狀況,油漆工又須要確認木工情況...
  
  你會發現他們必需要常常保持聯繫,以得到進展狀況,進而安排本身的工做
  
  一個包工隊尚且如此,若是是一個大的裝修公司,怎麼辦?
  
  並且裝修而言,階段之間還會有順序,油漆工用不到聯繫水電工
  
  可是在系統中,對象豈會僅僅與一個對象聯繫?
  
  那豈不是更復雜、亂套?
  
  中介者模式就是爲了解決系統內部的調度問題,下降系統內部各模塊之間的耦合度。
  
  裝修公司的項目經理、小組組長、班長,團隊leader等其實這都是中介者模式的體現。
  
  有不少書中以「房屋中介」做爲中介者模式的一種場景
  
  我的認爲對於某一個房東或者租客而言,「房屋中介」的含義是爲你服務的中介人員,此時的含義更接近代理模式
  
  而從廣義上看,有不少租客、買家,也存在不少房東,「房屋中介」將他們聯繫在一塊兒,此時的「房租中介」應該是中介公司,這時才更符合中介者模式的含義
  
  中介者模式的重點在於「調度、協調」,含義更接近「指揮中心」,被指揮的是該系統內部的成員
  
  若是在一個系統中對象之間存在多對多的相互關係
  
  咱們能夠將對象之間的一些交互行爲從各個對象中分離出來,並集中封裝在一箇中介者對象中,並由該中介者進行統一協調
  
  image_5c171b2a_66af
  
  如上圖所示,對象之間多對多的複雜關係就轉化爲相對簡單的一對多關係
  
  簡化了對象之間的複雜交互
  
  顯然,中介者模式是迪米特法則(不要和陌生人說話)的典型。
  
  結構
  
  image_5c171b2a_260e
  
  同事角色Colleague
  
  系統中全部組成部件的抽象角色
  
  具體的同事角色ConcreteColleague
  
  系統的每個具體的組成部分,就像公司的每一個同事
  
  提供自身職責功能的方法接口,供中介者調用
  
  定義中介者到同事對象的接口,也就是提供接口給中介者調用
  
  中介者(項目經理)根據你的技能分配任務,也就是調用你的方法
  
  中介者角色Mediator
  
  定義了同事Colleague對象到中介者的接口,也就是全部同事通訊的接口(同事間的通訊藉助於中介者提供的這個方法)
  
  也就是提供一個方法給同事們調用,用來請求其餘同事協助協助,這個方法是中介者提供的
  
  這個方法典型的示例就是事件處理方法
  
  具體的中介者ConcreteMediator
  
  具體的中介者,實現Mediator定義的接口,協調各同事進行協做
  
  全部的成員之間,能夠相互協調工做,可是卻又不直接相互管理
  
  這些對象都與項目經理「中介者」進行緊密聯繫
  
  由項目經理進行工做協調,每一個組成部分就如同咱們項目組中的一個成員,也就是同事同樣,這也是上文中Colleague 角色的由來
  
  如何相互協調工做可是卻又不直接相互管理?好比
  
  複製代碼
  
  class A{
  
  void f(){
  
  //do sth
  
  B b = new B();
  
  b.g();
  
  }
  
  複製代碼
  
  上面僞代碼中
  
  類A有一個方法f ,作了一些事情以後,建立了一個B的對象b,而後調用b的方法g,作了一些處理
  
  這就是A與B的協做,A也同時具備管理B的職責
  
  若是轉換爲下面的形式,就是中介者模式
  
  A和B的協做不在具備對象管理關係,而是項目經理Mediator統一進行管理
  
  複製代碼
  
  class Mediator{
  
  A a = new A();
  
  B b = new B();
  
  void cooperation(){
  
  a.f();
  
  b.g();
  
  }
  
  }
  
  複製代碼
  
  代碼示例
  
  使用《設計模式 可複用面向對象軟件的基礎》中的例子爲原型
  
  考慮一個圖形用戶界面中對話框的實現。
  
  對話框使用一個窗口來展示一系列的窗口組件,好比按鈕菜單輸入域等
  
  好比下圖,IDEA的字體設置窗口,當進行Font字體設置時
  
  預覽區域內的字體將會發生變化
  
  右下角的Apply 應用按鈕將成爲可點擊狀態
  
  image_5c171b2b_350a
  
  一種可能的解決方法
  
  複製代碼
  
  package mediator.simple;
  
  /**
  
  * 設置字體類,提供字體設置方法.
  
  * 而且建立展現Display對象,調用reDisplay方法從新展現
  
  * 而且建立按鈕Button對象,調用applyButton方法使能應用按鈕
  
  */
  
  public class Font {
  
  public void setFont() {
  
  System.out.println("設置字體...");
  
  Display display = new Display();
  
  display.reDisplay();
  
  Button button = new Button();
  
  button.applyButton();
  
  }
  
  }
  
  複製代碼
  
  複製代碼
  
  package mediator.simple;
  
  public class Display {
  
  public void reDisplay() {
  
  System.out.println("字體從新展現...");
  
  }
  
  }
  
  複製代碼
  
  複製代碼
  
  package mediator.simple;
  
  public class Button {
  
  public void applyButton() {
  
  System.out.println("應用按鈕可用...");
  
  }
  
  }
  
  複製代碼
  
  複製代碼
  
  package mediator.simple;
  
  public class Test {
  
  public static void main(String[] args) {
  
  Font font = new Font();
  
  font.setFont();
  
  }
  
  }
  
  複製代碼
  
  image_5c171b2b_398c
  
  上面的示例很簡單
  
  爲了實現「點擊設置字體,選擇字體後預覽框字體的改變以及使能應用按鈕的功能」
  
  也就是聯動的功能
  
  設置字體後,分別建立展現和按鈕對象,調用對象的方法
  
  很顯然,字體不只操心本身的事情,還管理着展現Display和按鈕Button
  
  並且,若是直接點擊取消會發生什麼?一切將會還原,又伴隨着一系列的調用
  
  難道仍舊須要:「不只操心本身的事情,還要負責管理別人麼」?
  
  就像沒有項目經理的包工隊同樣了,既操心本身又要管理別人
  
  成了咱們上面所說的網狀結構,內部各個同事之間的耦合度極高
  
  重構中介者模式
  
  重構的業務邏輯:
  
  經過引入mediator中介者,做爲同事之間協做的中間人,提供operation()方法,用於同事間請求協助、事件處理
  
  每一個同事類都知道這個中介,因此在抽象角色Colleague中設置了Mediator屬性,構造方法注入,而且提供notifyEvent方法,封裝了mediator的operation()方法
  
  當具體的同事ConcreteColleague,執行操做後,須要其餘同事協做時,直接調用notifyEvent()方法
  
  每一個具體的同事提供自身的職責接口
  
  簡單地說就是,提供中介者「項目經理」mediator,提供事件處理方法
  
  全部的同事都只知道「項目經理」,若是有事須要其餘同事辦,就叫「項目經理」。
  
  Mediator抽象中介者角色定義了operation方法
  
  各個Colleague對象經過此方法進行通訊,接受參數類型爲Colleague的對象
  
  package mediator;
  
  public abstract class Mediator {
  
  abstract void operation(Colleague event);
  
  }
  
  Colleague抽象同事角色擁有Mediator,經過構造方法注入
  
  提供了notifyEvent方法,調用中介者的operation方法,而且將自身做爲參數
  
  複製代碼
  
  package mediator;
  
  public abstract class Colleague {
  
  private Mediator mediator;
  
  Colleague(Mediator mediator) {
  
  this.mediator = mediator;
  
  }
  
  public void notifyEvent() {
  
  mediator.operation(this);
  
  }
  
  }
  
  複製代碼
  
  複製代碼
  
  package mediator;
  
  public class Button extends Colleague {
  
  Button(Mediator mediator){
  
  super(mediator);
  
  }
  
  public void applyButton(www.dasheng178.com/  ) {
  
  System.out.println("應用按鈕可用...");
  
  }
  
  }
  
  複製代碼
  
  複製代碼
  
  package mediator;
  
  public class Display extends Colleague {
  
  Display(Mediator  www.michenggw.com/ mediator) {
  
  super(mediator);
  
  }
  
  public void reDisplay(www.mcyllpt.com/ ) {
  
  System.out.println("字體從新展現...");
  
  }
  
  }
  
  複製代碼
  
  複製代碼
  
  package mediator;
  
  public class Font extends Colleague {
  
  private String fontName;
  
  public String getFontName(www.gcyl152.com) {
  
  return fontName;
  
  }
  
  Font(Mediator mediator) {
  
  super(mediator);
  
  }
  
  public void changeFont() {
  
  System.out.yongshiyule178.com/ println("設置字體......");
  
  fontName = "微軟雅黑";
  
  notifyEvent();
  
  }
  
  }
  
  複製代碼
  
  ConcreteMediator實現了Mediator定義的接口
  
  而且內部維護三個對象
  
  若是事件類型是Font,那麼調用設置字體的事件
  
  複製代碼
  
  package mediator;
  
  public class ConcreteMediator extends Mediator {
  
  private Button button;
  
  private Display display;
  
  private Font font;
  
  ConcreteMediator() {
  
  button = new Button(this);
  
  display = new Display(this);
  
  font = new Font(this);
  
  }
  
  @Override
  
  void operation(Colleague event) {
  
  if (event instanceof Font) {
  
  setFontEvent(event);
  
  }
  
  }
  
  private void setFontEvent(Colleague event) {
  
  System.out.println(((Font) event).getFontName());
  
  button.applyButton();
  
  display.reDisplay();
  
  }
  
  }
  
  複製代碼
  
  測試代碼
  
  複製代碼
  
  package mediator;
  
  public class Test {
  
  public static void main(String[] args){
  
  Mediator mediator = new ConcreteMediator();
  
  Font font = new Font(mediator);
  
  font.changeFont();
  
  }
  
  }
  
  複製代碼
  
  image_5c171b2b_3c66
  
  上面的示例中,以設置字體爲例,當字體變化時,請求「項目經理」安排其餘同事協助
  
  「項目經理」operation(Colleague event)  發現是設置字體的事件後,調用對應的事件處理方法,也就是尋找其餘同事進行協助
  
  中介者模式將每一個場景中對象之間的協做進行封裝
  
  小結
  
  當你須要其餘同事協助時,確定不須要項目經理每次都建立具體的同事對象
  
  上面的示例中,在ConcreteMediator的構造方法中建立的各個具體同事的實例(能夠理解爲項目經理手下有你和你的一幫同事)
  
  你還能夠提取出來工廠類用於管理同事類和中介者類
  
  好比經過一個工廠對象管理各個同事實例(若是能夠還能夠將各個同事都設置爲單例 )
  
  而且中介者類做爲這個工廠對象的內部類
  
  同事對象的傳遞能夠達到信息的獲取與回調
  
  經過給Font加了一個fontName屬性,經過打印信息能夠看得出來
  
  經過將當時發生事件的對象傳遞了過來,能夠得到事件的更多信息,能夠根據信息進一步的進行操做
  
  好比,此處的設置了字體,設置了什麼字體?這一進一步的信息就經過參數攜帶進來
  
  並且,這種方式還能夠通知到Font自己
  
  也就是能夠在setFontEvent(Colleague event) 事件處理中,調用Font的方法進行結果返回,有回調的韻味
  
  良好的擴展性
  
  若是須要增長一個新的事件處理過程,好比點擊取消按鈕,還原字體設置,還原預覽按鈕
  
  只須要在Button新增長一個職責(方法),而後在ConcreteMediator中新增長一種類型的事件處理程序便可
  
  上面的示例中僅僅定義了幾個簡單的方法,實踐中每一個具體同事角色天然不會僅僅只有幾個方法
  
  無論有多少方法,均可以經過中介者將他們的協做邏輯進行封裝
  
  經過具體的中介者ConcreteMediator的處理方法進行安排
  
  中介者模式能夠很好地應用於事件通知的處理
  
  中介者模式時序圖
  
  image_5c171b2b_1ce5
  
  當某一個同事對象產生事件後,若是須要其餘同事進行協助,他將調用中介者的方法
  
  中介者接到消息後,調用其餘同事的方法(安排其餘同時幹活),而後最終還能夠將消息進行返回
  
  與門面模式對比
  
  image_5c171b2b_31b0
  
  門面模式和中介者模式都是經過中間類下降系統的耦合度
  
  可是門面模式是爲了子系統提供一個簡單一致的接口,客戶端透過門面類向子系統發送消息,能夠認爲消息的發送是單方向的
  
  客戶端--->門面--->子系統方法,子系統提供功能給客戶端
  
  門面是在外部客戶端和內部子系統之間
  
  中介者模式中,則是主要複雜系統內部多個對象之間的相互協做
  
  中介者是各個內部對象同事之間
  
  擴展
  
  任何模式都不是一成不變的,模式結構圖只是一種相對比較合適準確的方案
  
  可是涉及到具體的業務中,一切都是可變的
  
  中介者模式也有多種靈活性
  
  若是隻有一箇中介者對象,顯然抽象Mediator角色是能夠隱藏的
  
  那麼ConcreteMediator就兼顧了這個角色,提供通訊接口給同事角色
  
  中介者模式的本質含義是負責協調系統內部的多個交互的對象,將同事進行解耦
  
  也就是說實現共同的接口並非必須的
  
  而實際上,一個系統中協做的多個對象,極可能是不一樣的類型,若是去掉了抽象角色Colleague
  
  那麼就能夠將任意的有關聯的對象組織在一塊兒,經過中介者協同工做
  
  並且,也並不意味着必定要中介者持有同事角色,若是合適,直接建立中介者也並不是不能夠
  
  雖然上面提到你可使用另外的工廠管理,那也只是一種經常使用用法而已。
  
  只須要記住中介者的本質在於「行爲與協做的分離,中介者封裝了對象間的協做」
  
  總結
  
  中介者模式將對象的行爲和協做進行分離,使對象更加專一於對象的行爲,也就是自身的功能
  
  將協做分離出來封裝到中介者內部
  
  迪米特法則,不要和陌生人講話,只與你的朋友通訊,中介者模式是迪米特法則的典型應用
  
  經過引入中介者對象,在同事之間的請求調用中,增長了「項目經理」,若是有事搞不定,須要協助,那麼就找他
  
  每一個人只和項目經理對話,也就是僅僅和項目經理這個朋友聊天,其餘的同事都不理了    ̄□ ̄||
  
  中介者將網狀結構,轉換爲了星型結構
  
  將多對多的關係,轉換爲了一對多的關係
  
  因此中介者模式將系統中對象對其餘對象的引用數目降到最低,簡化了對象之間的交互。
  
  image_5c171b2b_3f1e
  
  中介者模式將各個同事對象之間進行解耦,增長新的中介者或者同事都比較方便,符合開閉原則
  
  MVC模式,也是一種典型的中介者模式,控制器負責視圖層和模型層的調度處理,是一個明顯的中介者。
  
  中介者模式將同事進行解耦,可是各個Colleague類必須同中介者進行交互
  
  更準確的說,解耦後的關係植入到了中介者自身,若是原來的同事對象間的相互調用很是複雜
  
  那麼,這個中介者也極可能很是的複雜難以維護
  
  換言之,同事間的複雜性與耦合性轉移到了中介者內部
  
  中介者內部對於各個同事之間的協調代碼不太可能複用,因此具體同事類的複用性也是以中介者的不可複用爲代價的
  
  若是系統中的各個對象之間存在複雜的引用關係,但願可以經過中間類將他們進行解耦
  
  或者系統中對象間的引用混亂,交互太多,致使難以進行類的複用
  
  你就能夠考慮中介者模式
  
  可是並不意味着任何涉及到多個類交互的地方都用中介者模式,若是本來並不複雜,使用中介者將會增長複雜度
  
  基本前提就是緊密耦合,好比出現了網狀結構
  
  原文地址:中介者模式 調停者 Mediator 行爲型 設計模式(二十一)設計模式

相關文章
相關標籤/搜索