天天看點

設計模式_中介者模式下

建立了兩個抽象類 AbstractMediator 和 AbstractColeague,每個對象隻是與中介者 Mediator 之間産

生依賴,與其他對象之間沒有直接的關系,AbstractMediator 的作用是把中介者的抽象定義,定義了一個

抽象方法 execute,我們來看源代碼:

public abstract class AbstractMediator {
protected Purchase purchase;
protected Sale sale;
protected Stock stock;
//構造函數
public AbstractMediator(){
purchase = new Purchase(this);
sale = new Sale(this);
stock = new Stock(this);
}
//中介者最重要的方法,叫做事件方法,處理多個對象之間的關系
public abstract void execute(String str,Object...objects);
}      

我們再來看具體的中介者,中介者可以根據業務的要求産生多個中介者(一般情況隻有一個中介者),

劃分各個終結者的職責。我們來看 Mediator 源碼:

public class Mediator extends AbstractMediator {
//中介者最重要的方法
public void execute(String str,Object...objects){
if(str.equals("purchase.buy")){ //采購電腦
this.buyComputer((Integer)objects[0]);
}else if(str.equals("sale.sell")){ //銷售電腦
this.sellComputer((Integer)objects[0]);
}else if(str.equals("sale.offsell")){ //折價銷售
this.offSell();
}else if(str.equals("stock.clear")){ //清倉處理
this.clearStock();
}
}
//采購電腦
private void buyComputer(int number){
int saleStatus = super.sale.getSaleStatus();
if(saleStatus>80){ //銷售情況良好
System.out.println("采購IBM電腦:"+number + "台");
super.stock.increase(number);
}else{ //銷售情況不好
int buyNumber = number/2; //折半采購
System.out.println("采購IBM電腦: "+buyNumber+ "台");
}
}
//銷售電腦
private void sellComputer(int number){
if(super.stock.getStockNumber()<number){ //庫存數量不夠銷售
super.purchase.buyIBMcomputer(number); 
}
super.stock.decrease(number);
}
//折價銷售電腦
private void offSell(){
System.out.println("折價銷售IBM電腦"+stock.getStockNumber()+"台");
}


//清倉處理
private void clearStock(){
//要求清倉銷售
super.sale.offSale();
//要求采購人員不要采購
super.purchase.refuseBuyIBM();
}
}      

中介者 Mediator 有定義了多個 Private 方法,其目标是處理各個對象之間的依賴關系,即是說把原有

一個對象要依賴多個對象的情況移到中介者的 Private 方法中實作,在實際項目中,一般的做法是中介者

按照職責進行劃分,每個中介者處理一個或多個類似的關聯請求。

我們再來看 AbstractColleague 源碼:

public abstract class AbstractColleague {
protected AbstractMediator mediator;
public AbstractColleague(AbstractMediator _mediator){
this.mediator = _mediator;
}
}
采購類 Purchase 的源碼如下:
public class Purchase extends AbstractColleague{
public Purchase(AbstractMediator _mediator){
super(_mediator);
}
//采購IBM型号的電腦
public void buyIBMcomputer(int number){
super.mediator.execute("purchase.buy", number);
}
//不在采購IBM電腦
public void refuseBuyIBM(){
System.out.println("不再采購IBM電腦");
}
}      

Purchase 類是不是簡化了很多,看着也清晰了很多,處理自己的職責,與外界有關系的事件處理則交

給了中介者來完成。再來看 Stock 類:

public class Stock extends AbstractColleague {
public Stock(AbstractMediator _mediator){
super(_mediator);
}
//剛開始有100台電腦
private static int COMPUTER_NUMBER =100;
//庫存增加
public void increase(int number){
COMPUTER_NUMBER = COMPUTER_NUMBER + number;
System.out.println("庫存數量為: "+COMPUTER_NUMBER);
}
//庫存降低
public void decrease(int number){
COMPUTER_NUMBER = COMPUTER_NUMBER - number;
System.out.println("庫存數量為: "+COMPUTER_NUMBER);
}
//獲得庫存數量
public int getStockNumber(){
return COMPUTER_NUMBER;
}
//存貨壓力大了,就要通知采購人員不要采購,銷售人員要盡快銷售
public void clearStock(){
System.out.println("清理存貨數量為: "+COMPUTER_NUMBER);
super.mediator.execute("stock.clear");
}
}      

以下為 Sale 類的源碼:

public class Sale extends AbstractColleague {
public Sale(AbstractMediator _mediator){
super(_mediator);
}

//銷售IBM型号的電腦
public void sellIBMComputer(int number){
super.mediator.execute("sale.sell", number);
System.out.println("銷售IBM電腦"+number+"台");
}
//回報銷售情況,0——100之間變化, 0代表根本就沒人賣, 100代表非常暢銷,出1一個賣一個
public int getSaleStatus(){
Random rand = new Random(System.currentTimeMillis());
int saleStatus = rand.nextInt(100);
System.out.println("IBM電腦的銷售情況為: "+saleStatus);
return saleStatus;
}
//折價處理
public void offSale(){
super.mediator.execute("sale.offsell");
}
}      

再來看場景類的變化:

public class Client {
public static void main(String[] args) {
AbstractMediator mediator = new Mediator();
//采購人員采購電腦
System.out.println("------采購人員采購電腦--------");
Purchase purchase = new Purchase(mediator);
purchase.buyIBMcomputer(100);
//銷售人員銷售電腦
System.out.println("\n------銷售人員銷售電腦--------");
Sale sale = new Sale(mediator);
sale.sellIBMComputer(1);
//庫房管理人員管理庫存
System.out.println("\n------庫房管理人員清庫處理--------");
Stock stock = new Stock(mediator);
stock.clearStock();
}

}      

在場景類中增加了一個中介者,然後分别傳遞到三個同僚類中,三個類都具有相同的特性:隻負責處

理自己的活動(行為),與自己無關的活動就丢給中介者處理,程式運作的結果是相同的。從項目設計上來

看,加入了中介者,設計結構清晰了很多,而且類間的耦合性大大減少,代碼品質也有了很大的提升。

以上講解的模式就是中介者模式,在多個對象依賴的情況喜愛,通過加入中介者角色,取消了多個對

象的關聯或依關系,減少了對象的耦合性,其通用類圖為:

Mediator

ConcreteMediator

Colleague

從類圖中看,中介者模式有以下幾部分組成:

抽象中介者(Mediator)角色:抽象中介者角色定義統一的接口用于各同僚角色之間的通信。

具體中介者(Concrete Mediator)角色:具體中介者角色通過協調各同僚角色實作協作行為,是以它

必須依賴于各個同僚角色。

同僚(Colleague)角色:每一個同僚角色都知道中介者角色,而且與其他的同僚角色通信的時候,一

定要通過中介者角色協作。每個同僚類的行為分為兩種:一種是同僚本身的行為,比如改變對象本身的狀

态,處理自己的行為等等,這種方法叫做自發行為(Self-Method),與其他的同僚類或中介者沒有任何的依

賴;第二種是是必須依賴中介者才能完成的行為,叫做依賴方法(Dep-Method)。

在前幾張的講解中,每個模式的類都已經給出,但是卻沒有給出通用的源代碼,感覺這種方式不是很

好,是以從本章開始,都加入通用源代碼。中介者的通用源碼結構如下所示,先看中介者 Mediator 類:

public abstract class Mediator {
//定義同僚類
protected ConcreteColleague1 c1;
protected ConcreteColleague2 c2;
//通過getter/setter方法把同僚類注入進來
public ConcreteColleague1 getC1() {
return c1;

}
public void setC1(ConcreteColleague1 c1) {
this.c1 = c1;
}
public ConcreteColleague2 getC2() {
return c2;
}
public void setC2(ConcreteColleague2 c2) {
this.c2 = c2;
}      

//中介者模式的業務邏輯

public abstract void doSomething1();
public abstract void doSomething2();
}      

在 Mediator 抽象類中我們隻定義了同僚類的注入,為什麼使用同僚實作類注入而不使用抽象類注入

呢?那是因為同僚類雖然有抽象,但是這個抽象也太抽象了,根本就沒有每個同僚類所必須要完成的業務

方法,當然如果每個同僚類都有相同的方法,比如 execute,handler 等,那當然注入抽象類,做到依賴倒

轉。

具體的中介者一般隻有一個,其源碼如下:

public class ConcreteMediator extends Mediator {
@Override
public void doSomething1() {
//調用同僚類的方法,隻要是public方法都可以調用
super.c1.selfMethod1();
super.c2.selfMethod2();
}
public void doSomething2() {
super.c1.selfMethod1();
super.c2.selfMethod2();
}
}      

中介者所具有的方法 doSomething1 和 doSomething2 都是比較複雜的業務邏輯,都是為同僚類服務的,

其實作是依賴了各個同僚類來完成。

同僚類的基類如下:

public abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator _mediator){
this.mediator = _mediator;
}
}      

這個基類也是非常簡單的,一般來說中介者模式中的抽象都是比較簡單,是為了建立這個中介而服務

的,同僚實作類的源碼如下:

public class ConcreteColleague1 extends Colleague {
//通過構造函數傳遞中介者
public ConcreteColleague1(Mediator _mediator){
super(_mediator);
}
//自有方法 self-method
public void selfMethod1(){
//處理自己的業務邏輯
}
//依賴方法 dep-method
public void depMethod1(){
//處理自己的業務邏輯
//自己不能處理的業務邏輯,委托給中介者處理
super.mediator.doSomething1();
}
}
public class ConcreteColleague2 extends Colleague {
//通過構造函數傳遞中介者
public ConcreteColleague2(Mediator _mediator){
super(_mediator);

}
//自有方法 self-method
public void selfMethod2(){
//處理自己的業務邏輯
}
//依賴方法 dep-method
public void depMethod2(){
//處理自己的業務邏輯
//自己不能處理的業務邏輯,委托給中介者處理
super.mediator.doSomething2();
}
}      

為什麼同僚類要使用構造函數注入中介者而中介者使用 getter/setter 方式注入同僚類呢?想過沒

有?那是因為同僚類必須有中介者,而中介者可以隻有部分同僚類。

中介者模式的優點就是減少類間的依賴,把原有的一對多的依賴變成了一對一的依賴,同僚類隻依賴

中介者,減少了依賴,當然也同時減低了類間的耦合。它的缺點呢就是中介者會膨脹的很大,而且邏輯會

很複雜,因為所有的原本 N 個對象直接的互相依賴關系轉換為中介者和同僚類的依賴關系,同僚類越多,

中介者的邏輯就複雜。

中介者模式簡單,但是簡單不代表容易使用,這是 23 個模式中最容被誤用的模式。我們知道在面向對

象的程式設計中,對象和對象之間必然會有依賴關系,如果你寫了一個類,這個類和其他類沒有任何的依賴關

系,其他類也不依賴這個類,那這個類就是一個“孤島”嘛,在項目中就沒有必要存在!這就像是人類,

如果一個人永遠獨立生活,與任何人都沒有關系,那這個人基本上就算是野人了——排除在人類這個定義

之外。類之間的依賴關系是必然存在的,一個類依賴多個類的情況也是存在的,存在即合理,那是否可以

說隻要有多個依賴關系就考慮使用中介者模式呢?答案是否定的,中介者模式未必就能幫你把原本淩亂的

邏輯整理的清清楚楚,而且中介者模式也是有缺點的,這個缺點在不當的使用時會被放大,比如原本就簡

單的幾個對象依賴關系,如果為了使用模式而加入了中介者,必然導緻中介者的邏輯複雜化,是以中介者

模式的使用需要“量力而行”,那在什麼環境下才使用中介者模式呢?中介者模式适用于多個對象之間緊密

耦合,耦合的标準可以這樣來衡量:在類圖中出現了蜘蛛網狀結構,在這種情況下一定要考慮使用中介者

模式,有利于把蜘蛛網梳理為一個星型結構,使原本複雜混亂關系變得清晰簡單。

中介者模式也叫做調停者模式,是什麼意思呢?一個對象要和 N 多個對象交流,是不是就像對象間的

戰争,很混亂,你中有我,我中有你,那怎麼才能調停這種沖突呢?加入一個中心,所有的類都和中心交

流,中心說怎麼處理就這麼處理,我們舉一些在開發和生活中經常會碰到例子,再舉四個例子如下:

機場排程中心。大家在每個機場都會看到有一個叫做“XX 機場排程中心”,這個是做什麼用的呢?就是

具體的中介者,排程每一架要降落和起飛的飛機,一架分機(同僚類)飛到機場上空了,就詢問(同僚類

方法)排程中心(中介者)“我是否可以降落”,“降落到那個跑道”,然後排程中心(中介者)檢視其他飛

機(同僚類)情況,通知飛機降落,我們來設想一下,如果沒有機場排程中心會是什麼樣子的:飛機到機

場了,自己要先看看有沒有飛機和自己一起降落的,有沒有空跑道,停機位是否具備等等情況,這不是要

了飛行員老命才怪!

MVC 架構。大家都應該使用過 Struts 吧,MVC 架構,其中的 C(Controller)就是一個中介者,叫做前

端控制器(Front Controller),它的作用就是把 M(Model,業務邏輯)和 V(View,視圖)隔離開,協調 M

和 V 協同工作,把 M 運作的結果和 V 代表的視圖融合成一個前端可以展示的頁面,減少 M 和 V 的依賴關系。

MVC 架構已經成為一個非常流行、成熟的開發架構,這也是中介者模式優秀的一個展現。

C/S 結構。C/S 結構的應用也是一個典型的中介者模式,比如 MSN,張三發一個消息給李四,其過程應該

是這樣的:張三發送消息,MSN 伺服器(中介者)接受到消息,查找李四,把消息發送到李四,同時通知張三,

消息已經發送,在這裡 MSN 伺服器就是一個中轉站,負責協調兩個用戶端的資訊交流,與此相反的就是 IPMSG

(也叫飛鴿)沒有使用中介者,直接使用了 UDP 廣播的方式,每個用戶端既是用戶端也是服務端。

中介服務。現在中介服務非常多,比如租房中介,出國中介,這些也都是中介模式的具體展現,比如

你去租房子,如果沒有房屋中介,你就必須一個一個小區的找,看看有沒有空房子,有沒有适合自己的房子,找到房子後還要和房東簽合約,自己檢查房屋的家具、水電煤等,有了中介後,你就省心多了,找中

介,然後安排看房子,看中了,簽合約,中介幫你檢查房屋家具、水電煤等等。這也是中介模式的實際應

用。

不知道大家有沒有發覺,這章講的中介者模式很少用到接口或者抽象類,這與依賴倒轉原則是沖突的,

确實是,這是什麼原因呢?首先說同僚類,既然是同僚類而不是兄弟類(有相同的血緣)那也說明這些類

之間是協作關系,完成不同的任務,處理不同的業務,是以不能在抽象類或接口中嚴格定義同僚類必須具

有的方法(從這點也可以看出繼承是侵入性的),這是不合适的,就像你我是同僚,雖然我們大家都是朝九

晚五的上班,但是你跟我幹的活肯定不同,不可能抽象出一個父類統一定義同僚所必須有的方法,當然也

有辦法定義,每個同僚都要吃飯,都要上廁所,那把這些最基本的資訊封裝到抽象中是可以的,但問題是

這些最基本行為或屬性是中介者模式要關心的嘛?如果兩個對象不能提煉出共性,那就不要刻意的去追求

兩者的抽象,抽象隻要定義出模式需要的角色即可。其次是中介者的原因,在一個項目中中介者模式可能

被多個子產品采用,每個中介者所圍繞的同僚類各不相同,你能抽象出一個具有共性的中介者嗎?不可能,

一個中介者抽象類一般隻有一個實作者,除非中介者邏輯非常大,代碼量非常高,這時才會出現多個中介

繼續閱讀