設計模式系列之「中介者模式」

YoungManSter發表於2018-02-01

小Y在文章開始之前先回顧一下歷史:三省六部制是西晉以後長期發展形成,至隋朝正式確立,唐朝進一步完善的一種政治制度,反映了中國古代君主專制中央集權制度的進一步完善。那麼小Y今天的主題就來了—如何最大實現“一省六部”(尚書省、吏部、戶部、禮部、兵部、刑部、工部)的效能,Action。

一、前提

尚書省管屬下的六部之間相互協調工作,來張圖展示一下這錯綜複雜、愛恨情仇的關係。

設計模式系列之「中介者模式」

為了文章的簡潔,小Y不得不裁減部門了,把“一省六部”直接縮減為“一省三部”。

設計模式系列之「中介者模式」

三個部門相互依賴的同時它們也有著自己的職能,小Y就有失偏頗地概括了一下:

  • 戶部:管理錢財。
  • 兵部:掌管兵權。
  • 工部:掌管營造工程事項。

二、情景再現

1.天災造成百姓流離失所,餓殍滿地,這下戶部麻煩了,心想著責任不能我一個人擔啊,死都要拉個墊背的。

  • 對兵部說:饑民太多,恐防出現暴亂,需要軍隊鎮壓。

  • 對工部說:災害損毀房屋太多,需要你們來規劃重建。

2.不長眼睛的蠻夷族要攻打天朝,兵部這下就坐不住了,立馬找來工部和戶部。

  • 對工部說:蠻夷族的戰鬥力太強悍了,需要通過一些防禦工事抵禦,這事就交給你們了。

  • 對戶部說:要想打勝仗,士兵首先要吃得飽穿得暖,吃穿的錢糧這個重任就非你們莫屬了。

3.皇帝老爺的妃子多了,行宮要大批大批的建,工部就抱怨了,為毛皇帝這毛快活就要累死我們,不行,有苦同吃,有難各自飛,這才是兄弟嘛。

  • 對兵部說:陛下要求大規模建行宮,為陛下服務的機會到了哈,調遣大批士兵給我當苦力吧。

  • 對戶部說:行官需要設計精美,需要花費的銀子不少,需要你們搜刮多點民脂民膏。

三、出謀劃策

  • 方案一:尚書省只掛個管理牌就可以,對下面六個部門的工作不干預,愛咋幹咋幹,關鍵時刻給我功勞就好。

  • 以尚書省為中心,底下六個部門在工作上不直接進行交流,統一經過尚書 省(中介者模式)。

四、來波廣告

1.中介模式的定義

用一箇中介物件封裝一系列的物件互動,中介者使各物件不需要顯示地相互作用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。

2.中介模式的角色介紹

設計模式系列之「中介者模式」

  • Mediator 抽象中介者角色
    抽象中介者角色定義統一的介面,用於各同事角色之間的通訊。

  • Concrete Mediator 具體中介者角色
    具體中介者角色通過協調各同事角色實現協作行為,因此它必須依賴於各個同事角色。

  • Colleague 同事角色
    每一個同事角色都知道中介者角色,而且與其他的同事角色通訊的時候,一定要通過中介者角色協作。每個同事類的行為分為兩種:一種是同事本身的行為,比如改變物件本身的狀態,處理自己的行為等,這種行為叫做自發行為,與其他的同事類或中介者沒有任何的依賴;第二種是必須依賴中介者才能完成的行為,叫做依賴方法。

3.中介模式的使用場景

  • 在物件導向的程式設計中,物件和物件之間必然會有依賴關係。

  • 中介者模式適用於多個物件之間緊密耦合的情況,緊密耦合的標準是:在類圖中出現了蜘蛛網狀結構。

五、方案的實施

1.方案一

發生事情各個部門自行商量解決。

①戶部

public class Department {

	public void dealDisaster(){
		System.out.println("戶部:專挑輕活,其他的找別人幹去。");
		//需要工部規劃重建
		Ministry ministry=new Ministry();
		ministry.selfFunction();
		//需要兵部調兵遣將鎮壓災民
		Defense defense=new Defense();
		defense.selfFunction();

	}

	public void selfFunction(){
		System.out.println("戶部:要錢沒問題,我儘量搜刮點民脂民膏。");
	}
}
複製程式碼

②兵部

public class Defense {

	public void fight(){
		System.out.println("我只出人,剩下的找別人幹去。");
		//需要戶部搜刮民脂民膏
		Department department=new Department();
		department.selfFunction();
		//需要工部給圖紙建造防禦工事
		Ministry ministry=new Ministry();
		ministry.selfFunction();
	}

	public void selfFunction(){
		System.out.println("兵部:要人沒問題,我儘量抓多幾個壯丁");
	}
}
複製程式碼

③工部

public class Ministry {

	public void buildPalace(){
		System.out.println("工部:我只畫圖紙,其他的找別人幹去。");
		//需要戶部出錢
		Department department=new Department();
		department.selfFunction();
		//需要兵部調兵遣將加入建造
		Defense defense=new Defense();
		defense.selfFunction();
	}

	public void selfFunction(){
		System.out.println("工部:要建築圖紙沒問題,我儘量複製多幾份");
	}
}
複製程式碼

④Client

public class Client {
	public static void main(String[] args) {
		//發生天災了,戶部麻煩了,需要解決問題
		Department department=new Department();
		department.dealDisaster();
		//要打仗了,兵部的活來了
		Defense defense=new Defense();
		defense.fight();
		//皇帝發話了,工部趕緊建行宮
		Ministry ministry=new Ministry();
		ministry.buildPalace();
	}
}
複製程式碼

輸出的結果為:

//發生天災了,戶部麻煩了,需要解決問題
戶部:專挑輕活,其他的找別人幹去。
工部:要建築圖紙沒問題,我儘量複製多幾份。
兵部:要人沒問題,我儘量抓多幾個壯丁。

//要打仗了,兵部的活來了
兵部:我只出人,剩下的找別人幹去。
戶部:要錢沒問題,我儘量搜刮點民脂民膏。
工部:要建築圖紙沒問題,我儘量複製多幾份。

//皇帝發話了,工部趕緊建行宮
工部:我只畫圖紙,其他的找別人幹去。
戶部:要錢沒問題,我儘量搜刮點民脂民膏。
兵部:要人沒問題,我儘量抓多幾個壯丁。
複製程式碼

三個部門遇到問題都相互協調解決了,沒出什麼么蛾子,尚書省也照樣得到功勞獎勵。從上面的例子發現這三個類是彼此關聯的,每個類都與其他兩個類產生了關聯關係。這樣子缺點就暴露出來了,它們彼此關聯越多,耦合性越大,要想修改一個就得修改一片,這不是物件導向設計所期望的,上面的例子還是僅三個部門的情況,如果實現上圖的六部之間的協調關係,維護起來都要吐血而亡,果斷拋棄。

2.方案二(中介者模式)

UML實現

設計模式系列之「中介者模式」

①抽象中介者(尚書省)
public abstract class AbstractMediator {

	protected Department department;
	protected Defense defense;
	protected Ministry ministry;

	public AbstractMediator() {
		department = new Department(this);
		defense=new Defense(this);
		ministry=new Ministry(this);
	}
	
	//中介者最重要的方法叫做事件方法,處理多個物件之間的關係
	public abstract void dealThing(int code);
}
複製程式碼
②具體中介者
public class Mediator extends AbstractMediator{

	public static final int DEPARTMENT_CODE=1;
	public static final int DEFENSE_CODE=2;
	public static final int MINISTRY_CODE=3;

	@Override
	public void dealThing(int code) {
		switch (code){
			case DEPARTMENT_CODE:
				this.dealDisaster();
				break;
			case DEFENSE_CODE:
				this.fight();
				break;
			case MINISTRY_CODE:
				this.buildPalace();
				break;
		}
	}
	//戶部處理天災
	private void dealDisaster(){
		System.out.println("戶部:專挑輕活,其他的找別人幹去。");
		super.ministry.selfFunction();
		super.defense.selfFunction();
	}
	//兵部打仗
	private void fight(){
		System.out.println("兵部:我只出人,剩下的找別人幹去。");
		super.department.selfFunction();
		super.ministry.selfFunction();
	}
	//工部建行宮
	private void buildPalace(){
		System.out.println("工部:我只畫圖紙,其他的找別人幹去。");
		super.department.selfFunction();
		super.defense.selfFunction();
	}
}
複製程式碼
③抽象部門類
public abstract class AbstractColleague {
	protected AbstractMediator abstractMediator;

	public AbstractColleague(AbstractMediator abstractMediator) {
		this.abstractMediator = abstractMediator;
	}
}
複製程式碼
④戶部
public class Department extends AbstractColleague{

	public Department(AbstractMediator 	abstractMediator) {
	super(abstractMediator);
	}

	public void dealDisaster(){
		super.abstractMediator.dealThing(Mediator.DEPARTMENT_CODE);
	}

	public void selfFunction(){
		System.out.println("要錢沒問題,我儘量搜刮點民脂民膏。");
	}
}
複製程式碼
⑤兵部
public class Defense extends AbstractColleague{

	public Defense(AbstractMediator abstractMediator) {
		super(abstractMediator);
	}

	public void fight(){
		super.abstractMediator.dealThing(Mediator.MINISTRY_CODE);
	}

	public void selfFunction(){
		System.out.println("兵部:要人沒問題,我儘量抓多幾個壯丁");
	}
}
複製程式碼
⑥工部
public class Ministry extends AbstractColleague{

	public Ministry(AbstractMediator abstractMediator) {
		super(abstractMediator);
	}

	public void buildPalace(){
		super.abstractMediator.dealThing(Mediator.MINISTRY_CODE);
	}

	public void selfFunction(){
		System.out.println("要建築圖紙沒問題,我儘量複製多幾份");
	}
}
複製程式碼
⑦Client
public class Client {
	public static void main(String[] args) {
		AbstractMediator abstractMediator=new Mediator();
		//發生天災了,戶部麻煩了,需要解決問題
		Department department=new Department(abstractMediator);
		department.dealDisaster();
		//要打仗了,兵部的活來了
		Defense defense=new Defense(abstractMediator);
		defense.fight();
		//皇帝發話了,工部趕緊建行宮
		Ministry ministry=new Ministry(abstractMediator);
		ministry.buildPalace();
	}
}
複製程式碼

輸出的結果為:

//發生天災了,戶部麻煩了,需要解決問題
戶部:專挑輕活,其他的找別人幹去。
工部:要建築圖紙沒問題,我儘量複製多幾份。
兵部:要人沒問題,我儘量抓多幾個壯丁。

//要打仗了,兵部的活來了
兵部:我只出人,剩下的找別人幹去。
戶部:要錢沒問題,我儘量搜刮點民脂民膏。
工部:要建築圖紙沒問題,我儘量複製多幾份。

//皇帝發話了,工部趕緊建行宮
工部:我只畫圖紙,其他的找別人幹去。
戶部:要錢沒問題,我儘量搜刮點民脂民膏。
兵部:要人沒問題,我儘量抓多幾個壯丁。
複製程式碼
(1)建立了兩個抽象類AbstractMediator和AbstractColeague,每個物件只是與中介者Mediator之間產生依賴,與其他物件之間沒有直接關係。
(2)中介者Mediator定義了多個private方法,其目的是處理各個物件之間的依賴關係,就是說把原有一個物件要依賴多個物件的情況移到中介者的private方法中實現。
(3)在場景類中增加了一箇中介者,然後分別傳遞到三個同事類中,三個類都具有相同的特性:只負責處理自己的活動(行為),與自己無關的活動就丟給中介者處理。
(4)在多個物件依賴的情況下,通過加入中介者角色,取消了多個物件的關聯或依賴關係,減少了物件的耦合性。

六、中介者模式優缺點

1.優點

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

2.缺點

中介者模式的缺點就是中介者會膨脹得很大,而且邏輯複雜,原本N個物件直接的相互依賴關係轉換為中介者和同事類的依賴關係,同事類越多,中介者的邏輯就越複雜。

七、簡單對比觀察者模式和中介者模式的區別

1.多個物件之間需要互動,那麼這些物件間的互動就會形成網狀結構。引入中介者,各物件根本不知道其它物件的存在,他們只需要把資訊傳送給中介者,由中介者來控制把資訊傳遞給哪些物件。
2.觀察者依賴於被觀察者的狀態改變,兩者之間是需要互動的。

八、總結

中介者模式是一個非常好的封裝模式,也是一個很容易被濫用的模式,要根據實際情況考慮是否選用中介模式,使用中介模式就必然會帶來中介者的膨脹問題,這一點一定要清楚。

Android技術交流吧

相關文章