JAVA設計模式(22) —<行為型>中介者模式(Mediator)


定義:

中介者模式(Mediator)

Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.(用一個中介對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。)

1.1 通用類圖:


    在中介者模式結構圖中包含如下幾個角色:

       Mediator(抽象中介者)它定義一個接口,該接口用於與各同事對象之間進行通信。

       ConcreteMediator(具體中介者)它是抽象中介者的子類,通過協調各個同事對象來實現協作行為,它維持了對各個同事對象的引用。

       Colleague(抽象同事類)它定義各個同事類公有的方法,並聲明了一些抽象方法來供子類實現,同時它維持了一個對抽象中介者類的引用,其子類可以通過該引用來與中介者通信。

       ConcreteColleague(具體同事類)它是抽象同事類的子類;每一個同事對象在需要和其他同事對象通信時,先與中介者通信,通過中介者來間接完成與其他同事類的通信;在具體同事類中實現了在抽象同事類中聲明的抽象方法。

 

      中介者模式的核心在於中介者類的引入,在中介者模式中,中介者類承擔了兩方面的職責:

       (1) 中轉作用(結構性):通過中介者提供的中轉作用,各個同事對象就不再需要顯式引用其他同事,當需要和其他同事進行通信時,可通過中介者來實現間接調用。該中轉作用屬於中介者在結構上的支持。

      (2) 協調作用(行為性):中介者可以更進一步的對同事之間的關系進行封裝,同事可以一致的和中介者進行交互,而不需要指明中介者需要具體怎么做,中介者根據封裝在自身內部的協調邏輯,對同事的請求進行進一步處理,將同事成員之間的關系行為進行分離和封裝。該協調作用屬於中介者在行為上的支持。


1.2中介模式的作用

如果在一個系統中對象之間的聯系呈現為網狀結構,如圖20-4所示。對象之間存在大量的多對多聯系,將導致系統非常復雜,這些對象既會影響別的對象,也會被別的對象所影響,這些對象稱為同事對象,它們之間通過彼此的相互作用實現系統的行為。在網狀結構中,幾乎每個對象都需要與其他對象發生相互作用,而這種相互作用表現為一個對象與另外一個對象的直接耦合,這將導致一個過度耦合的系統。

20-4 對象之間存在復雜關系的網狀結構

      中介者模式可以使對象之間的關系數量急劇減少,通過引入中介者對象,可以將系統的網狀結構變成以中介者為中心的星形結構,如圖20-5所示。在這個星形結構中,同事對象不再直接與另一個對象聯系,它通過中介者對象與另一個對象發生相互作用。中介者對象的存在保證了對象結構上的穩定,也就是說,系統的結構不會因為新對象的引入帶來大量的修改工作。

20-5 引入中介者對象的星型結構

      如果在一個系統中對象之間存在多對多的相互關系,我們可以將對象之間的一些交互行為從各個對象中分離出來,並集中封裝在一個中介者對象中,並由該中介者進行統一協調,這樣對象之間多對多的復雜關系就轉化為相對簡單的一對多關系。通過引入中介者來簡化對象之間的復雜交互,中介者模式是“迪米特法則”的一個典型應用


1.3 通用代碼:

[java] view plaincopy
  1. public abstract class Mediator {  
  2.     // 定義同事類  
  3.     protected ConcreteColleague1 c1;  
  4.     protected ConcreteColleague2 c2;  
  5.   
  6.     // 通過getter/setter方法吧同事類注入進來  
  7.     public ConcreteColleague1 getC1() {  
  8.         return c1;  
  9.     }  
  10.   
  11.     public void setC1(ConcreteColleague1 c1) {  
  12.         this.c1 = c1;  
  13.     }  
  14.   
  15.     public ConcreteColleague2 getC2() {  
  16.         return c2;  
  17.     }  
  18.   
  19.     public void setC2(ConcreteColleague2 c2) {  
  20.         this.c2 = c2;  
  21.     }  
  22.   
  23.     // 中介者模式的業務邏輯  
  24.     public abstract void doSomething1();  
  25.   
  26.     public abstract void doSomething2();  
  27. }  
  28.   
  29. public class ConcreteMediator extends Mediator {  
  30.     @Override  
  31.     public void doSomething1() {  
  32.         // 調用同事類的方法,只要是public方法都可以調用  
  33.         super.c1.selfMethod1();  
  34.         super.c2.selfMethod2();  
  35.     }  
  36.   
  37.     public void doSomething2() {  
  38.         super.c1.selfMethod1();  
  39.         super.c2.selfMethod2();  
  40.     }  
  41. }  
  42.   
  43. public abstract class Colleague {  
  44.     protected Mediator mediator;  
  45.   
  46.     public Colleague(Mediator _mediator) {  
  47.         this.mediator = _mediator;  
  48.     }  
  49. }  
  50.   
  51. public class ConcreteColleague1 extends Colleague {  
  52.     // 通過構造函數傳遞中介者  
  53.     public ConcreteColleague1(Mediator _mediator) {  
  54.         super(_mediator);  
  55.     }  
  56.   
  57.     // 自有方法 self-method  
  58.     public void selfMethod1() {  
  59.         // 處理自己的業務邏輯  
  60.     }  
  61.   
  62.     // 依賴方法 dep-method  
  63.     public void depMethod1() {  
  64.         // 處理自己的業務邏輯  
  65.         // 自己不能處理的業務邏輯,委托給中介者處理  
  66.         super.mediator.doSomething1();  
  67.     }  
  68. }  
  69.   
  70. public class ConcreteColleague2 extends Colleague {  
  71.     // 通過構造函數傳遞中介者  
  72.     public ConcreteColleague2(Mediator _mediator) {  
  73.         super(_mediator);  
  74.     }  
  75.   
  76.     // 自有方法 self-method  
  77.     public void selfMethod2() {  
  78.         // 處理自己的業務邏輯  
  79.     }  
  80.   
  81.     // 依賴方法 dep-method  
  82.     public void depMethod2() {  
  83.         // 處理自己的業務邏輯  
  84.         // 自己不能處理的業務邏輯,委托給中介者處理  
  85.         super.mediator.doSomething2();  
  86.     }  
  87. }  

注意:

Mediator抽象類中我們只定義了同事類的get/set注入,為什么不使用抽象注入呢?這是因為同事類雖然有抽象,但是沒有每個同事類必須要完成的業務方法。當然如果每個同事類都有相同的方法,比如execute handler等,那當然注入抽象類,做到依賴倒置。

為什么同事類要使用構造函數注入中介者,而中介者注入同事類只使用getter/setter方式呢?因為同事類必須有中介者,而中介者卻可以只有部分同事類

優點

減少類間的依賴,把原有的一對多的依賴變成了一對一依賴,同事類只依賴中介者,減少了依賴,當然同時也降低了類間的耦合。

缺點

中介者會膨脹得很大,而且邏輯復雜,原本N個對象直接的相互依賴關系轉換為中介者和同事類的依賴關系,同事類越多,中介者的邏輯就越復雜。

應用場景

類之間的聯系是必然的,並非有多個依賴時就要使用中介者模式。中介者模式只適用於多個對象間緊密耦合的情況,緊密耦合的標准是:在類圖中出現了蜘蛛網狀結構。在這種情況下一定要考慮使用中介者模式,這有利於把蜘蛛網梳理為星型結構,使原本復雜混亂的關系變得清晰簡單。

4.1 符合上述緊密耦合標准時使用;

4.2 N個對象之間產生了相互的依賴關系(N>2);

4.3 多個對象有依賴關系,但是依賴的行為沿不確定或者有發生改變的可能,在這種情況下一般建議采用中介者模式,降低變更引起的風險擴散;

4.4 產品開發。一個明顯的例子就是MVC框架,把中介者模式應用到產品中,可以提升產品的性能和擴展性,但是對於項目開發就未必,因為項目是以交付投產為目標,而產品則是以穩定、高效、擴展為宗旨。

生活中的使用情境:

A。機場調度中心;

BMVC框架;

C。媒體網關;(如MSN通過服務器轉發消息,而飛鴿直接使用UDP廣播卻不是。)

D。各種中介服務;

注意事項

5.1 遵循緊密耦合的標准來用中介者模式;

5.2 中介者模式很少用到接口或抽象類,這與依賴倒置原則是沖突的:首先,既然是同事類而不是兄弟類(有相同的血緣),那就說明這些類之間是協作關系,完成不同的任務,處理不同的業務,所以不能在抽象類或接口中嚴格定義同事類必須具有的方法。因此,如果兩個對象不能提煉出共性,就不要刻意去追求兩者的抽象,抽象只要定義出模式需要的角色即可

其次,在一個項目中使用中介者模式可能被多個模塊采用,每個中介者所圍繞的同事類各不相同,抽象出一個具有共性的中介者也不太可能。

5.3 中介者模式是一種比較常用的模式,也是一種比較容易被濫用的模式。對於大多數的情況,同事類之間的關系不會復雜到混亂不堪的網狀結構,因此,大多數情況下,將對象間的依賴關系封裝的同事類內部就可以的,沒有必要非引入中介者模式。濫用中介者模式,只會讓事情變的更復雜

擴展

中介者模式將一個網狀的系統結構變成一個以中介者對象為中心的星形結構,在這個星型結構中,使用中介者對象與其他對象的一對多關系來取代原有對象之間的多對多關系。中介者模式在事件驅動類軟件中應用較為廣泛,特別是基於GUIGraphical User Interface,圖形用戶界面)的應用軟件,此外,在類與類之間存在錯綜復雜的關聯關系的系統中,中介者模式都能得到較好的應用。

范例

7.1 進銷存案例

這三者間已形成了網狀關系,加入了一個中介者作為三個模塊的交流核心每個模塊之間不再相互交流,要交流就通過中介者進行。每個模塊只負責自己的業務邏輯,不屬於自己的則丟給中介者來處理,簡化了各模塊之間的耦合關系,類圖如下:


[java] view plaincopy
  1.     /** 
  2.      * @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you 
  3.      *         all. 
  4.      */  
  5.     public abstract class AbstractMediator {  
  6.         protected Purchase purchase;  
  7.         protected Sale sale;  
  8.         protected Stock stock;  
  9.   
  10.         // 構造函數  
  11.         public AbstractMediator() {  
  12.             purchase = new Purchase(this);  
  13.             sale = new Sale(this);  
  14.             stock = new Stock(this);  
  15.         }  
  16.   
  17.         // 中介者最重要的方法,叫做事件方法,處理多個對象之間的關系  
  18.         public abstract void execute(String str, Object... objects);  
  19.     }  
  20.   
  21.     public class Mediator extends AbstractMediator {  
  22.         // 中介者最重要的方法  
  23.         public void execute(String str, Object... objects) {  
  24.             if (str.equals("purchase.buy")) { // 采購電腦  
  25.                 this.buyComputer((Integer) objects[0]);  
  26.             } else if (str.equals("sale.sell")) { // 銷售電腦  
  27.                 this.sellComputer((Integer) objects[0]);  
  28.             } else if (str.equals("sale.offsell")) { // 折價銷售  
  29.                 this.offSell();  
  30.             } else if (str.equals("stock.clear")) { // 清倉處理  
  31.                 this.clearStock();  
  32.             }  
  33.         }  
  34.   
  35.         // 采購電腦  
  36.         private void buyComputer(int number) {  
  37.             int saleStatus = super.sale.getSaleStatus();  
  38.             if (saleStatus > 80) { // 銷售情況良好  
  39.                 System.out.println("采購IBM電腦:" + number + "台");  
  40.                 super.stock.increase(number);  
  41.             } else { // 銷售情況不好  
  42.                 int buyNumber = number / 2// 折半采購  
  43.                 System.out.println("采購IBM電腦:" + buyNumber + "台");  
  44.             }  
  45.         }  
  46.   
  47.         // 銷售電腦  
  48.         private void sellComputer(int number) {  
  49.             if (super.stock.getStockNumber() < number) { // 庫存數量不夠銷售  
  50.                 super.purchase.buyIBMcomputer(number);  
  51.             }  
  52.             super.stock.decrease(number);  
  53.         }  
  54.   
  55.         // 折價銷售電腦  
  56.         private void offSell() {  
  57.             System.out.println("折價銷售IBM電腦" + stock.getStockNumber() + "台");  
  58.         }  
  59.   
  60.         // 清倉處理  
  61.         private void clearStock() {  
  62.             // 要求清倉銷售  
  63.             super.sale.offSale();  
  64.             // 要求采購人員不要采購  
  65.             super.purchase.refuseBuyIBM();  
  66.         }  
  67.     }  

中介者Mediator定義了多個private方法,其目的是處理各個對象之間的依賴關系,就是說把原有一個對象要依賴的多個對象的情況移動到中介者的private方法中實現

但在實際項目中,一般的做法是中介者按照職責進行划分,每個中介者處理一個或多個類似的關聯請求。

[java] view plaincopy
  1. public abstract class AbstractColleague {  
  2.         protected AbstractMediator mediator;  
  3.   
  4.         public AbstractColleague(AbstractMediator _mediator) {  
  5.             this.mediator = _mediator;  
  6.         }  
  7.     }  
  8.   
  9.     public class Purchase extends AbstractColleague {  
  10.         public Purchase(AbstractMediator _mediator) {  
  11.             super(_mediator);  
  12.         }  
  13.   
  14.         // 采購IBM型號的電腦  
  15.         public void buyIBMcomputer(int number) {  
  16.             super.mediator.execute("purchase.buy", number);  
  17.         }  
  18.   
  19.         // 不在采購IBM電腦  
  20.         public void refuseBuyIBM() {  
  21.             System.out.println("不再采購IBM電腦");  
  22.         }  
  23.     }  
  24.   
  25.     public class Stock extends AbstractColleague {  
  26.         public Stock(AbstractMediator _mediator) {  
  27.             super(_mediator);  
  28.         }  
  29.   
  30.         // 剛開始有100台電腦  
  31.         private static int COMPUTER_NUMBER = 100;  
  32.   
  33.         // 庫存增加  
  34.         public void increase(int number) {  
  35.             COMPUTER_NUMBER = COMPUTER_NUMBER + number;  
  36.             System.out.println("庫存數量為:" + COMPUTER_NUMBER);  
  37.         }  
  38.   
  39.         // 庫存降低  
  40.         public void decrease(int number) {  
  41.             COMPUTER_NUMBER = COMPUTER_NUMBER - number;  
  42.             System.out.println("庫存數量為:" + COMPUTER_NUMBER);  
  43.         }  
  44.   
  45.         // 獲得庫存數量  
  46.         public int getStockNumber() {  
  47.             return COMPUTER_NUMBER;  
  48.         }  
  49.   
  50.         // 存貨壓力大了,就要通知采購人員不要采購,銷售人員要盡快銷售  
  51.         public void clearStock() {  
  52.             System.out.println("清理存貨數量為:" + COMPUTER_NUMBER);  
  53.             super.mediator.execute("stock.clear");  
  54.         }  
  55.     }  
  56.   
  57.     public class Sale extends AbstractColleague {  
  58.         public Sale(AbstractMediator _mediator) {  
  59.             super(_mediator);  
  60.         }  
  61.   
  62.         // 銷售IBM型號的電腦  
  63.         public void sellIBMComputer(int number) {  
  64.             super.mediator.execute("sale.sell", number);  
  65.             System.out.println("銷售IBM電腦" + number + "台");  
  66.         }  
  67.   
  68.         // 反饋銷售情況,0——100之間變化,0代表根本就沒人賣,100代表非常暢銷,出1一個賣一個  
  69.         public int getSaleStatus() {  
  70.             Random rand = new Random(System.currentTimeMillis());  
  71.             int saleStatus = rand.nextInt(100);  
  72.             System.out.println("IBM電腦的銷售情況為:" + saleStatus);  
  73.             return saleStatus;  
  74.         }  
  75.   
  76.         // 折價處理  
  77.         public void offSale() {  
  78.             super.mediator.execute("sale.offsell");  
  79.         }  
  80.     }  
  81.   
  82.     public class Client {  
  83.         public static void main(String[] args) {  
  84.             AbstractMediator mediator = new Mediator();  
  85.             // 采購人員采購電腦  
  86.             System.out.println("------采購人員采購電腦--------");  
  87.             Purchase purchase = new Purchase(mediator);  
  88.             purchase.buyIBMcomputer(100);  
  89.             // 銷售人員銷售電腦  
  90.             System.out.println("\n------銷售人員銷售電腦--------");  
  91.             Sale sale = new Sale(mediator);  
  92.             sale.sellIBMComputer(1);  
  93.             // 庫房管理人員管理庫存  
  94.             System.out.println("\n------庫房管理人員清庫處理--------");  
  95.             Stock stock = new Stock(mediator);  
  96.             stock.clearStock();  
  97.         }  
  98.     }  

在場景類中增加了一個中介者,然后分別傳遞到三個同事類中,三個類都具有相同的特性:只負責處理自己的活動(行為),與自己無關的活動就丟給中介者處理。

7.2租房案例

以租房為例,這里中介機構充當租房者與房屋所有者之間的中介者。UML結構圖:


       首先是抽象中介者:Mediator.java

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. public abstract class Mediator {  
  2.     //申明一個聯絡方法  
  3.     public abstract void constact(String message,Person person);  
  4. }  

       然后是抽象同事對象:Person.java

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. public abstract class Person {  
  2.     protected String name;  
  3.     protected Mediator mediator;  
  4.       
  5.     Person(String name,Mediator mediator){  
  6.         this.name = name;  
  7.         this.mediator = mediator;  
  8.     }  
  9.       
  10. }  

       兩個具體同事類:HouseOwner.java

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. public class HouseOwner extends Person{  
  2.   
  3.     HouseOwner(String name, Mediator mediator) {  
  4.         super(name, mediator);  
  5.     }  
  6.       
  7.     /** 
  8.      * @desc 與中介者聯系 
  9.      * @param message 
  10.      * @return void 
  11.      */  
  12.     public void constact(String message){  
  13.         mediator.constact(message, this);  
  14.     }  
  15.   
  16.     /** 
  17.      * @desc 獲取信息 
  18.      * @param message 
  19.      * @return void 
  20.      */  
  21.     public void getMessage(String message){  
  22.         System.out.println("房主:" + name +",獲得信息:" + message);  
  23.     }  
  24. }  

       Tenant.java

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. public class Tenant extends Person{  
  2.       
  3.     Tenant(String name, Mediator mediator) {  
  4.         super(name, mediator);  
  5.     }  
  6.       
  7.     /** 
  8.      * @desc 與中介者聯系 
  9.      * @param message 
  10.      * @return void 
  11.      */  
  12.     public void constact(String message){  
  13.         mediator.constact(message, this);  
  14.     }  
  15.   
  16.     /** 
  17.      * @desc 獲取信息 
  18.      * @param message 
  19.      * @return void 
  20.      */  
  21.     public void getMessage(String message){  
  22.         System.out.println("租房者:" + name +",獲得信息:" + message);  
  23.     }  
  24. }  

       具體中介者對象:中介結構、MediatorStructure.java

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. public class MediatorStructure extends Mediator{  
  2.     //首先中介結構必須知道所有房主和租房者的信息  
  3.     private HouseOwner houseOwner;  
  4.     private Tenant tenant;  
  5.   
  6.     public HouseOwner getHouseOwner() {  
  7.         return houseOwner;  
  8.     }  
  9.   
  10.     public void setHouseOwner(HouseOwner houseOwner) {  
  11.         this.houseOwner = houseOwner;  
  12.     }  
  13.   
  14.     public Tenant getTenant() {  
  15.         return tenant;  
  16.     }  
  17.   
  18.     public void setTenant(Tenant tenant) {  
  19.         this.tenant = tenant;  
  20.     }  
  21.   
  22.     public void constact(String message, Person person) {  
  23.         if(person == houseOwner){          //如果是房主,則租房者獲得信息  
  24.             tenant.getMessage(message);  
  25.         }  
  26.         else{       //反正則是房主獲得信息  
  27.             houseOwner.getMessage(message);  
  28.         }  
  29.     }  
  30. }  

       客戶端:Client.java

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. public class Client {  
  2.     public static void main(String[] args) {  
  3.         //一個房主、一個租房者、一個中介機構  
  4.         MediatorStructure mediator = new MediatorStructure();  
  5.           
  6.         //房主和租房者只需要知道中介機構即可  
  7.         HouseOwner houseOwner = new HouseOwner("張三", mediator);  
  8.         Tenant tenant = new Tenant("李四", mediator);  
  9.           
  10.         //中介結構要知道房主和租房者  
  11.         mediator.setHouseOwner(houseOwner);  
  12.         mediator.setTenant(tenant);  
  13.           
  14.         tenant.constact("聽說你那里有三室的房主出租.....");  
  15.         houseOwner.constact("是的!請問你需要租嗎?");  
  16.     }  
  17. }  

       運行結果:

       房主:張三,獲得信息:聽說你那里有三室的房主出租..... 

       租房者:李四,獲得信息:是的!請問你需要租嗎?

7.3管理頁面UI組件管理

  Sunny軟件公司欲開發一套CRM系統,其中包含一個客戶信息管理模塊,所設計的“客戶信息管理窗口”界面效果圖如圖20-2所示:

20-2 “客戶信息管理窗口”界面圖

       Sunny公司開發人員通過分析發現,在圖20-2中,界面組件之間存在較為復雜的交互關系:如果刪除一個客戶,要在客戶列表(List)中刪掉對應的項,客戶選擇組合框(ComboBox)中客戶名稱也將減少一個;如果增加一個客戶信息,客戶列表中需增加一個客戶,且組合框中也將增加一項。

       如果實現界面組件之間的交互是Sunny公司開發人員必須面對的一個問題?

        Sunny公司開發人員對組件之間的交互關系進行了分析,結果如下:

       (1) 當用戶單擊“增加”按鈕、“刪除”按鈕、“修改”按鈕或“查詢”按鈕時,界面左側的“客戶選擇組合框”、“客戶列表”以及界面中的文本框將產生響應。

       (2) 當用戶通過“客戶選擇組合框”選中某個客戶姓名時,“客戶列表”和文本框將產生響應。

       (3) 當用戶通過“客戶列表”選中某個客戶姓名時,“客戶選擇組合框”和文本框將產生響應。

      於是,Sunny公司開發人員根據組件之間的交互關系繪制了如圖20-3所示初始類圖:

20-3 “客戶信息管理窗口”原始類圖

      與類圖20-3所對應的框架代碼片段如下:

[java] view plaincopy
  1. //按鈕類  
  2. class Button {  
  3.     private List list;  
  4.     private ComboBox cb;  
  5.     private TextBox tb;  
  6.     ......  
  7.   
  8.     //界面組件的交互  
  9.     public void change() {  
  10.         list.update();  
  11.         cb.update();  
  12.         tb.update();  
  13.     }  
  14.   
  15.     public void update() {  
  16.         ......  
  17.     }  
  18.     ......  
  19. }  
  20.   
  21. //列表框類  
  22. class List {  
  23.     private ComboBox cb;  
  24.     private TextBox tb;  
  25.     ......  
  26.   
  27. //界面組件的交互  
  28.     public void change() {  
  29.         cb.update();  
  30.         tb.update();  
  31.     }  
  32.   
  33.     public void update() {  
  34.         ......  
  35.     }  
  36.     ......    
  37. }  
  38.   
  39. //組合框類  
  40. class ComboBox {  
  41.     private List list;  
  42.     private TextBox tb;  
  43.     ......  
  44.   
  45. //界面組件的交互  
  46.     public void change() {  
  47.         list.update();  
  48.         tb.update();  
  49.     }  
  50.   
  51.     public void update() {  
  52.         ......  
  53.     }  
  54.     ......    
  55. }  
  56.   
  57. //文本框類  
  58. class TextBox {  
  59.     public void update() {  
  60.         ......  
  61.     }  
  62.     ......    
  63. }  

        分析圖20-3所示初始結構圖和上述代碼,我們不難發現該設計方案存在如下問題:

       (1) 系統結構復雜且耦合度高:每一個界面組件都與多個其他組件之間產生相互關聯和調用,若一個界面組件對象發生變化,需要跟蹤與之有關聯的其他所有組件並進行處理,系統組件之間呈現一種較為復雜的網狀結構,組件之間的耦合度高。

       (2) 組件的可重用性差:由於每一個組件和其他組件之間都具有很強的關聯,若沒有其他組件的支持,一個組件很難被另一個系統或模塊重用,這些組件表現出來更像一個不可分割的整體,而在實際使用時,我們往往需要每一個組件都能夠單獨重用,而不是重用一個由多個組件組成的復雜結構。

       (3) 系統的可擴展性差:如果在上述系統中增加一個新的組件類,則必須修改與之交互的其他組件類的源代碼,將導致多個類的源代碼需要修改,同樣,如果要刪除一個組件也存在類似的問題,這違反了“開閉原則”,可擴展性和靈活性欠佳。

       由於存在上述問題,Sunny公司開發人員不得不對原有系統進行重構,那如何重構呢?大家想到了“迪米特法則”,引入一個“第三者”來降低現有系統中類之間的耦合度。由這個“第三者”來封裝並協調原有組件兩兩之間復雜的引用關系,使之成為一個松耦合的系統,這個“第三者”又稱為“中介者”,中介者模式因此而得名。下面讓我們正式進入中介者模式的學習,學會如何使用中介者類來協調多個類/對象之間的交互,以達到降低系統耦合度的目的。

完整解決方案

為了協調界面組件對象之間的復雜交互關系,Sunny公司開發人員使用中介者模式來設計客戶信息管理窗口,其結構示意圖如圖20-7所示:

20-7 引入了中介者類的“客戶信息管理窗口”結構示意圖

      20-7只是一個重構之后的結構示意圖,在具體實現時,為了確保系統具有更好的靈活性和可擴展性,我們需要定義抽象中介者和抽象組件類,其中抽象組件類是所有具體組件類的公共父類,完整類圖如圖20-8所示:

20-8 重構后的“客戶信息管理窗口”結構圖

      在圖20-8中,Component充當抽象同事類,ButtonListComboBoxTextBox充當具體同事類,Mediator充當抽象中介者類,ConcreteMediator充當具體中介者類,ConcreteMediator維持了對具體同事類的引用,為了簡化ConcreteMediator類的代碼,我們在其中只定義了一個Button對象和一個TextBox對象。完整代碼如下所示:

[java] view plaincopy
  1. //抽象中介者  
  2. abstract class Mediator {  
  3.     public abstract void componentChanged(Component c);  
  4. }  
  5.   
  6. //具體中介者  
  7. class ConcreteMediator extends Mediator {  
  8.     //維持對各個同事對象的引用  
  9.     public Button addButton;  
  10.     public List list;  
  11.     public TextBox userNameTextBox;  
  12.     public ComboBox cb;  
  13.   
  14.     //封裝同事對象之間的交互  
  15.     public void componentChanged(Component c) {  
  16.         //單擊按鈕  
  17. if(c == addButton) {  
  18.             System.out.println("--單擊增加按鈕--");  
  19.             list.update();  
  20.             cb.update();  
  21.             userNameTextBox.update();  
  22.         }  
  23.         //從列表框選擇客戶  
  24.         else if(c == list) {  
  25.             System.out.println("--從列表框選擇客戶--");  
  26.             cb.select();  
  27.             userNameTextBox.setText();  
  28.         }  
  29.         //從組合框選擇客戶  
  30.         else if(c == cb) {  
  31.             System.out.println("--從組合框選擇客戶--");  
  32.             cb.select();  
  33.             userNameTextBox.setText();  
  34.         }  
  35.     }  
  36. }  
  37.   
  38. //抽象組件類:抽象同事類  
  39. abstract class Component {  
  40.     protected Mediator mediator;  
  41.       
  42.     public void setMediator(Mediator mediator) {  
  43.         this.mediator = mediator;  
  44.     }  
  45.   
  46.     //轉發調用  
  47.     public void changed() {  
  48.         mediator.componentChanged(this);  
  49.     }  
  50.       
  51.     public abstract void update();    
  52. }  
  53.   
  54. //按鈕類:具體同事類  
  55. class Button extends Component {  
  56.     public void update() {  
  57.         //按鈕不產生交互  
  58.     }  
  59. }  
  60.   
  61. //列表框類:具體同事類  
  62. class List extends Component {  
  63.     public void update() {  
  64.         System.out.println("列表框增加一項:張無忌。");  
  65.     }  
  66.       
  67.     public void select() {  
  68.         System.out.println("列表框選中項:小龍女。");  
  69.     }  
  70. }  
  71.   
  72. //組合框類:具體同事類  
  73. class ComboBox extends Component {  
  74.     public void update() {  
  75.         System.out.println("組合框增加一項:張無忌。");  
  76.     }  
  77.       
  78.     public void select() {  
  79.         System.out.println("組合框選中項:小龍女。");  
  80.     }  
  81. }  
  82.   
  83. //文本框類:具體同事類  
  84. class TextBox extends Component {  
  85.     public void update() {  
  86.         System.out.println("客戶信息增加成功后文本框清空。");  
  87.     }  
  88.       
  89.     public void setText() {  
  90.         System.out.println("文本框顯示:小龍女。");  
  91.     }  
  92. }  

      編寫如下客戶端測試代碼:

[java] view plaincopy
  1. class Client {  
  2.     public static void main(String args[]) {  
  3.         //定義中介者對象  
  4.         ConcreteMediator mediator;  
  5.         mediator = new ConcreteMediator();  
  6.           
  7.         //定義同事對象  
  8.         Button addBT = new Button();  
  9.         List list = new List();  
  10.         ComboBox cb = new ComboBox();  
  11.         TextBox userNameTB = new TextBox();  
  12.   
  13.         addBT.setMediator(mediator);  
  14.         list.setMediator(mediator);  
  15.         cb.setMediator(mediator);  
  16.         userNameTB.setMediator(mediator);  
  17.   
  18.         mediator.addButton = addBT;  
  19.         mediator.list = list;  
  20.         mediator.cb = cb;  
  21.         mediator.userNameTextBox = userNameTB;  
  22.           
  23.         addBT.changed();  
  24.         System.out.println("-----------------------------");  
  25.         list.changed();  
  26.     }  
  27. }  

      編譯並運行程序,輸出結果如下:

--單擊增加按鈕--

列表框增加一項:張無忌。

組合框增加一項:張無忌。

客戶信息增加成功后文本框清空。

-----------------------------

--從列表框選擇客戶--

組合框選中項:小龍女。

文本框顯示:小龍女。

中介者與同事類的擴展

  Sunny軟件公司CRM系統的客戶對“客戶信息管理窗口”提出了一個修改意見:要求在窗口的下端能夠及時顯示當前系統中客戶信息的總數。修改之后的界面如圖20-9所示:

20-9 修改之后的“客戶信息管理窗口”界面圖

       從圖20-9中我們不難發現,可以通過增加一個文本標簽(Label)來顯示客戶信息總數,而且當用戶點擊“增加”按鈕或者“刪除”按鈕時,將改變文本標簽的內容。

      由於使用了中介者模式,在原有系統中增加新的組件(即新的同事類)將變得很容易,我們至少有如下兩種解決方案:

       【解決方案一】增加一個界面組件類Label,修改原有的具體中介者類ConcreteMediator,增加一個對Label對象的引用,然后修改componentChanged()方法中其他相關組件對象的業務處理代碼,原有組件類無須任何修改,客戶端代碼也需針對新增組件Label進行適當修改。

       【解決方案二】與方案一相同,首先增加一個Label類,但不修改原有具體中介者類ConcreteMediator的代碼,而是增加一個ConcreteMediator的子類SubConcreteMediator來實現對Label對象的引用,然后在新增的中介者類SubConcreteMediator中通過覆蓋componentChanged()方法來實現所有組件(包括新增Label組件)之間的交互,同樣,原有組件類無須做任何修改,客戶端代碼需少許修改。

      引入Label之后“客戶信息管理窗口”類結構示意圖如圖20-10所示:

20-10 增加Label組件類后的“客戶信息管理窗口”結構示意圖

       由於【解決方案二】無須修改ConcreteMediator類,更符合“開閉原則”,因此我們選擇該解決方案來對新增Label類進行處理,對應的完整類圖如圖20-11所示:

20-11 修改之后的“客戶信息管理窗口”結構圖

      在圖20-11中,新增了具體同事類Label和具體中介者類SubConcreteMediator,代碼如下所示:

[java] view plaincopy
  1. //文本標簽類:具體同事類  
  2. class Label extends Component {  
  3.     public void update() {  
  4.         System.out.println("文本標簽內容改變,客戶信息總數加1。");  
  5.     }  
  6. }  
  7.   
  8. //新增具體中介者類  
  9. class SubConcreteMediator extends ConcreteMediator {  
  10.     //增加對Label對象的引用  
  11.     public Label label;  
  12.       
  13.     public void componentChanged(Component c) {  
  14.         //單擊按鈕  
  15. if(c == addButton) {  
  16.             System.out.println("--單擊增加按鈕--");  
  17.             list.update();  
  18.             cb.update();  
  19.             userNameTextBox.update();  
  20.             label.update(); //文本標簽更新  
  21.         }  
  22.         //從列表框選擇客戶  
  23.         else if(c == list) {  
  24.             System.out.println("--從列表框選擇客戶--");  
  25.             cb.select();  
  26.             userNameTextBox.setText();  
  27.         }  
  28.         //從組合框選擇客戶  
  29.         else if(c == cb) {  
  30.             System.out.println("--從組合框選擇客戶--");  
  31.             cb.select();  
  32.             userNameTextBox.setText();  
  33.         }  
  34.     }  
  35. }  

       修改客戶端測試代碼:

[java] view plaincopy
  1. class Client {  
  2.     public static void main(String args[]) {  
  3.         //用新增具體中介者定義中介者對象  
  4.         SubConcreteMediator mediator;  
  5.         mediator = new SubConcreteMediator();  
  6.           
  7.         Button addBT = new Button();  
  8.         List list = new List();  
  9.         ComboBox cb = new ComboBox();  
  10.         TextBox userNameTB = new TextBox();  
  11.         Label label = new Label();  
  12.   
  13.         addBT.setMediator(mediator);  
  14.         list.setMediator(mediator);  
  15.         cb.setMediator(mediator);  
  16.         userNameTB.setMediator(mediator);  
  17.         label.setMediator(mediator);  
  18.           
  19.         mediator.addButton = addBT;  
  20.         mediator.list = list;  
  21.         mediator.cb = cb;  
  22.         mediator.userNameTextBox = userNameTB;  
  23.         mediator.label = label;  
  24.               
  25.         addBT.changed();  
  26.         System.out.println("-----------------------------");  
  27.         list.changed();  
  28.     }  
  29. }  

       編譯並運行程序,輸出結果如下:

--單擊增加按鈕--

列表框增加一項:張無忌。

組合框增加一項:張無忌。

客戶信息增加成功后文本框清空。

文本標簽內容改變,客戶信息總數加1

-----------------------------

--從列表框選擇客戶--

組合框選中項:小龍女。

文本框顯示:小龍女。

       由於在本實例中不同的組件類(即不同的同事類)所擁有的方法並不完全相同,因此中介者類沒有針對抽象同事類編程,導致在具體中介者類中需要維持對具體同事類的引用,客戶端代碼無法完全透明地對待所有同事類和中介者類。在某些情況下,如果設計得當,可以在客戶端透明地對同事類和中介者類編程,這樣系統將具有更好的靈活性和可擴展

  在中介者模式的實際使用過程中,如果需要引入新的具體同事類,只需要繼承抽象同事類並實現其中的方法即可,由於具體同事類之間並無直接的引用關系,因此原有所有同事類無須進行任何修改,它們與新增同事對象之間的交互可以通過修改或者增加具體中介者類來實現如果需要在原有系統中增加新的具體中介者類,只需要繼承抽象中介者類(或已有的具體中介者類)並覆蓋其中定義的方法即可,在新的具體中介者中可以通過不同的方式來處理對象之間的交互,也可以增加對新增同事的引用和調用。在客戶端中只需要修改少許代碼(如果引入配置文件的話有時可以不修改任何代碼)就可以實現中介者的更換。



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com