#0 系列目錄#編程
#1 場景問題# ##1.1 若是沒有主板## 你們都知道,電腦裏面各個配件之間的交互,主要是經過主板來完成的(事實上主板有不少的功能,這裏不去討論)。試想一下,若是電腦裏面沒有主板,會怎樣呢?設計模式
若是電腦裏面沒有了主板,那麼各個配件之間就必須自行相互交互,以互相傳送數據,理論上說,基本上各個配件相互之間都存在交互數據的可能。如圖10.1所示:ide
這也太複雜了吧,這還沒完呢,因爲各個配件的接口不一樣,那麼相互之間交互的時候,還必須把數據接口進行轉換才能匹配上,那就更恐怖了
。測試
所幸是有了主板,各個配件的交互徹底經過主板來完成,每一個配件都只須要和主板交互,而主板知道如何和全部的配件打交道
,那就簡單多了,這就避免瞭如圖10.1所描述的那樣亂做一團,有主板後的結構如圖10.2所示:ui
##1.2 有何問題## 若是上面的狀況發生在軟件開發上呢?this
若是把每一個電腦配件都抽象成爲一個類或者是子系統,那就至關於出現了多個類之間相互交互,並且交互還很繁瑣,致使每一個類都必須知道全部須要交互的類,也就是咱們常說的類和類耦合了
,是否是很麻煩?.net
在軟件開發中出現這種狀況可就不妙了,不但開發的時候每一個類會複雜,由於要兼顧其它的類,更要命的是每一個類在發生改動的時候,須要通知全部相關的類一塊兒修改
,由於接口或者是功能發生了變更,使用它的地方都得變,快要瘋了吧!設計
那該如何來簡化這種多個對象之間的交互呢?代理
##1.3 使用電腦來看電影## 爲了演示,考慮一個稍微具體點的功能。在平常生活中,咱們常用電腦來看電影,把這個過程描述出來,這裏僅僅考慮正常的狀況,也就是有主板的狀況,簡化後假定會有以下的交互過程:code
首先是光驅要讀取光盤上的數據,而後告訴主板,它的狀態改變了;
主板去獲得光驅的數據,把這些數據交給CPU進行分析處理;
CPU處理完後,把數據分紅了視頻數據和音頻數據,通知主板,它處理完了;
主板去獲得CPU處理事後的數據,分別把數據交給顯卡和聲卡,去顯示出視頻和發出聲音;
固然這是一個持續的、不斷重複的過程,從而造成不間斷的視頻和聲音,具體的運行過程不在討論之列,假設就有如上簡單的交互關係就能夠了。也就是說想看電影,把光盤放入光驅,光驅開始讀盤,就能夠看電影了。
如今要求使用程序把這個過程描述出來,該如何具體實現呢?
#2 解決方案# ##2.1 中介者模式來解決## 用來解決上述問題的一個合理的解決方案就是中介者模式。那麼什麼是中介者模式呢?
仔細分析上面的問題,根本緣由就在於多個對象須要相互交互,從而致使對象之間緊密耦合
,這就不利於對象的修改和維護。
中介者模式的解決思路很簡單,跟電腦的例子同樣,中介者模式經過引入一箇中介對象,讓其它的對象都只和中介對象交互,而中介對象知道如何和其它全部的對象交互
,這樣對象之間的交互關係就沒有了,從而實現對象之間的解耦。
對於中介對象而言,全部相互交互的對象,被視爲同事類,中介對象就是來維護各個同事之間的關係,而全部的同事類都只是和中介對象交互。
每一個同事對象,當本身發生變化的時候,不須要知道這會引發其它對象有什麼變化,它只須要通知中介者就能夠了,而後由中介者去與其它對象交互。這樣鬆散耦合帶來的好處是,除了讓同事對象之間相互沒有關聯外,還有利於功能的修改和擴展。
有了中介者事後,全部的交互都封裝到中介者對象裏面,各個對象就再也不須要維護這些關係了。擴展關係的時候也只須要擴展或修改中介者對象就能夠了
。
##2.2 模式結構和說明## 中介者模式的結構如圖10.3所示:
Mediator:中介者接口。
在裏面定義各個同事之間交互須要的方法,能夠是公共的通信方法
,好比changed方法,你們都用,也能夠是小範圍的交互方法。ConcreteMediator:具體中介者實現對象。
它須要瞭解並維護各個同事對象,並負責具體的協調各同事對象的交互關係
。Colleague:同事類的定義,
一般實現成爲抽象類,主要負責約束同事對象的類型,並實現一些具體同事類之間的公共功能
,好比:每一個具體同事類都應該知道中介者對象,也就是具體同事類都會持有中介者對象,就能夠定義到這個類裏面。ConcreteColleague:具體的同事類,實現本身的業務,在須要與其它同事通信的時候,
就與持有的中介者通訊,中介者會負責與其它的同事交互
。
##2.3 中介者模式示例代碼##
按照前面的描述,全部須要交互的對象,都被視爲同事類,這些同事類應該有一個統一的約束。並且全部的同事類都須要和中介者對象交互,換句話說就是全部的同事都應該持有中介者對象。
所以,爲了統一約束衆多的同事類,併爲同事類提供持有中介者對象的公共功能,先來定義一個抽象的同事類,在裏面實現持有中介者對象的公共功能。
要提醒一點,下面示例的這個抽象類是沒有定義抽象方法的,主要是用來約束全部同事類的類型,示例代碼以下:
/** * 同事類的抽象父類 */ public abstract class Colleague { /** * 持有中介者對象,每個同事類都知道它的中介者對象 */ private Mediator mediator; /** * 構造方法,傳入中介者對象 * @param mediator 中介者對象 */ public Colleague(Mediator mediator) { this.mediator = mediator; } /** * 獲取當前同事類對應的中介者對象 * @return 對應的中介者對象 */ public Mediator getMediator() { return mediator; } }
/** * 具體的同事類A */ public class ConcreteColleagueA extends Colleague { public ConcreteColleagueA(Mediator mediator) { super(mediator); } /** * 示意方法,執行某些業務功能 */ public void someOperation() { //在須要跟其它同事通訊的時候,通知中介者對象 getMediator().changed(this); } } /** * 具體的同事類B */ public class ConcreteColleagueB extends Colleague { public ConcreteColleagueB(Mediator mediator) { super(mediator); } /** * 示意方法,執行某些業務功能 */ public void someOperation() { //在須要跟其它同事通訊的時候,通知中介者對象 getMediator().changed(this); } }
/** * 中介者,定義各個同事對象通訊的接口 */ public interface Mediator { /** * 同事對象在自身改變的時候來通知中介者的方法, * 讓中介者去負責相應的與其它同事對象的交互 * @param colleague 同事對象自身,好讓中介者對象經過對象實例去獲取同事對象的狀態 */ public void changed(Colleague colleague); }
/** * 具體的中介者實現 */ public class ConcreteMediator implements Mediator { /** * 持有並維護同事A */ private ConcreteColleagueA colleagueA; /** * 持有並維護同事B */ private ConcreteColleagueB colleagueB; /** * 設置中介者須要瞭解並維護的同事A對象 * @param colleague 同事A對象 */ public void setConcreteColleagueA(ConcreteColleagueA colleague) { colleagueA = colleague; } /** * 設置中介者須要瞭解並維護的同事B對象 * @param colleague 同事B對象 */ public void setConcreteColleagueB(ConcreteColleagueB colleague) { colleagueB = colleague; } public void changed(Colleague colleague) { //某個同事類發生了變化,一般須要與其它同事交互 //具體協調相應的同事對象來實現協做行爲 } }
##2.4 使用中介者模式來實現示例## 要使用中介者模式來實現示例,那就要區分出同事對象和中介者對象。很明顯,主板是做爲中介者,而光驅、CPU、聲卡、顯卡等配件,都是做爲同事對象。
根據中介者模式的知識,設計出示例的程序結構如圖10.4所示:
public abstract class Colleague { private Mediator mediator; public Colleague(Mediator mediator) { this.mediator = mediator; } public Mediator getMediator() { return mediator; } }
定義好了同事的抽象父類,接下來就應該具體的實現這些同事類了,按照順序一個一個來,先看看光驅類吧,示例代碼以下:
/** * 光驅類,一個同事類 */ public class CDDriver extends Colleague{ public CDDriver(Mediator mediator) { super(mediator); } /** * 光驅讀取出來的數據 */ private String data = ""; /** * 獲取光驅讀取出來的數據 * @return 光驅讀取出來的數據 */ public String getData(){ return this.data; } /** * 讀取光盤 */ public void readCD(){ //逗號前是視頻顯示的數據,逗號後是聲音 this.data = "設計模式,值得好好研究"; //通知主板,本身的狀態發生了改變 this.getMediator().changed(this); } } /** * CPU類,一個同事類 */ public class CPU extends Colleague{ public CPU(Mediator mediator) { super(mediator); } /** * 分解出來的視頻數據 */ private String videoData = ""; /** * 分解出來的聲音數據 */ private String soundData = ""; /** * 獲取分解出來的視頻數據 * @return 分解出來的視頻數據 */ public String getVideoData() { return videoData; } /** * 獲取分解出來的聲音數據 * @return 分解出來的聲音數據 */ public String getSoundData() { return soundData; } /** * 處理數據,把數據分紅音頻和視頻的數據 * @param data 被處理的數據 */ public void executeData(String data){ //把數據分解開,前面的是視頻數據,後面的是音頻數據 String [] ss = data.split(","); this.videoData = ss[0]; this.soundData = ss[1]; //通知主板,CPU的工做完成 this.getMediator().changed(this); } } /** * 顯卡類,一個同事類 */ public class VideoCard extends Colleague{ public VideoCard(Mediator mediator) { super(mediator); } /** * 顯示視頻數據 * @param data 被顯示的數據 */ public void showData(String data){ System.out.println("您正觀看的是:"+data); } } /** * 聲卡類,一個同事類 */ public class SoundCard extends Colleague{ public SoundCard(Mediator mediator) { super(mediator); } /** * 按照聲頻數據發出聲音 * @param data 發出聲音的數據 */ public void soundData(String data){ System.out.println("畫外音:"+data); } }
因爲全部的同事對象都要和中介者交互,來定義出中介者的接口,功能很少,提供一個讓同事對象在自身改變的時候來通知中介者的方法,示例代碼以下:
/** * 中介者對象的接口 */ public interface Mediator { /** * 同事對象在自身改變的時候來通知中介者的方法, * 讓中介者去負責相應的與其它同事對象的交互 * @param colleague 同事對象自身,好讓中介者對象經過對象實例 * 去獲取同事對象的狀態 */ public void changed(Colleague colleague); }
中介者的功能就稍微多一點,它須要處理全部的同事對象之間的交互,好在咱們要示例的東西並不麻煩,僅有兩個功能處理而已,示例代碼以下:
/** * 主板類,實現中介者接口 */ public class MotherBoard implements Mediator{ /** * 須要知道要交互的同事類——光驅類 */ private CDDriver cdDriver = null; /** * 須要知道要交互的同事類——CPU類 */ private CPU cpu = null; /** * 須要知道要交互的同事類——顯卡類 */ private VideoCard videoCard = null; /** * 須要知道要交互的同事類——聲卡類 */ private SoundCard soundCard = null; public void setCdDriver(CDDriver cdDriver) { this.cdDriver = cdDriver; } public void setCpu(CPU cpu) { this.cpu = cpu; } public void setVideoCard(VideoCard videoCard) { this.videoCard = videoCard; } public void setSoundCard(SoundCard soundCard) { this.soundCard = soundCard; } public void changed(Colleague colleague) { if(colleague == cdDriver){ //表示光驅讀取數據了 this.opeCDDriverReadData((CDDriver)colleague); }else if(colleague == cpu){ //表示CPU處理完了 this.opeCPU((CPU)colleague); } } /** * 處理光驅讀取數據事後與其它對象的交互 * @param cd 光驅同事對象 */ private void opeCDDriverReadData(CDDriver cd){ //1:先獲取光驅讀取的數據 String data = cd.getData(); //2:把這些數據傳遞給CPU進行處理 this.cpu.executeData(data); } /** * 處理CPU處理完數據後與其它對象的交互 * @param cpu CPU同事類 */ private void opeCPU(CPU cpu){ //1:先獲取CPU處理事後的數據 String videoData = cpu.getVideoData(); String soundData = cpu.getSoundData(); //2:把這些數據傳遞給顯卡和聲卡展現出來 this.videoCard.showData(videoData); this.soundCard.soundData(soundData); } }
定義完了同事類,也實現處理了它們交互的中介者對象,該來寫個客戶端使用它們,來看個電影,好好享受一下。寫個客戶端測試一下,看看效果,示例代碼以下:
public class Client { public static void main(String[] args) { //1:建立中介者——主板對象 MotherBoard mediator = new MotherBoard(); //2:建立同事類 CDDriver cd = new CDDriver(mediator); CPU cpu = new CPU(mediator); VideoCard vc = new VideoCard(mediator); SoundCard sc = new SoundCard(mediator); //3:讓中介者知道全部的同事 mediator.setCdDriver(cd); mediator.setCpu(cpu); mediator.setVideoCard(vc); mediator.setSoundCard(sc); //4:開始看電影,把光盤放入光驅,光驅開始讀盤 cd.readCD(); } }
運行結果以下:
您正觀看的是:設計模式 畫外音:值得好好研究
如同上面的示例,對於光驅對象、CPU對象、顯卡對象和聲卡對象,須要相互交互,雖然只是簡單演示,可是也能看出來,它們的交互是比較麻煩的,因而定義一箇中介者對象——主板對象,來維護它們之間的交互關係,從而使得這些對象鬆散耦合
。
若是這個時候須要修改它們的交互關係,直接到中介者裏面修改就行了,也就是說它們的關係已經獨立封裝到中介者對象裏面了,能夠獨立的改變它們之間的交互關係,而不用去修改這些同事對象。
#3 模式講解# ##3.1 認識中介者模式##
中介者的功能很是簡單,就是封裝對象之間的交互。若是一個對象的操做會引發其它相關對象的變化,或者是某個操做須要引發其它對象的後續或連帶操做,而這個對象又不但願本身來處理這些關係,那麼就能夠找中介者,把全部的麻煩扔給它,只在須要的時候通知中介者,其它的就讓中介者去處理就能夠了。
反過來,其它的對象在操做的時候,可能會引發這個對象的變化,也能夠這麼作。最後對象之間就徹底分離了,誰都不直接跟其它對象交互,那麼相互的關係,所有被集中到中介者對象裏面了,全部的對象就只是跟中介者對象進行通訊,相互之間再也不有聯繫。
把全部對象之間的交互都封裝在中介者當中,無形中還獲得另一個好處,就是可以集中的控制這些對象的交互關係,這樣有什麼變化的時候,修改起來就很方便
。
要回答這個問題,先要搞清楚一件事情,接口用來幹什麼的?對,接口是用來實現「封裝隔離」的,那麼封裝誰?隔離誰呢?Mediator接口嘛,確定是用來封裝中介者對象的,使得使用中介者對象的客戶對象跟具體的中介者實現對象分離開。
瞭解了上面這些內容,回過來想一想,有沒有使用Mediator接口的必要,那就取決因而否會提供多個不一樣的中介者實現
。若是中介者實現只有一個的話,並且預計中也沒有須要擴展的要求,那麼就能夠不定義Mediator接口,讓各個同事對象直接使用中介者實現對象;若是中介者實現不僅一個,或者預計中有擴展的要求,那麼就須要定義Mediator接口,讓各個同事對象來面向中介者接口編程,而無需關心具體的中介者實現
。
在標準的中介者模式中,把使用中介者對象來交互的那些對象稱爲同事類,這不是亂叫的,在中介者模式中,要求這些類都要繼承相同的類
,也就是說,這些對象從某個角度講是同一個類型,算是兄弟對象。
正是這些兄弟對象之間的交互關係很複雜,才產生了把這些交互關係分離出去,單獨作成中介者對象,這樣一來,這些兄弟對象就成了中介者對象眼裏的同事。
在中介者模式中,當一個同事對象發生了改變,須要主動通知中介者,讓中介者去處理與其它同事對象相關的交互。
這就致使了同事對象和中介者對象之間必須有關係,首先是同事對象須要知道中介者對象是誰
;反過來,中介者對象也須要知道相關的同事對象
,這樣它才能與同事對象進行交互。也就是說中介者對象和同事對象之間是相互依賴的。
一個同事對象發生了改變,會通知中介者對象,中介者對象會處理與其它同事的交互,這就產生了同事對象和中介者對象的相互通訊。怎麼實現這種通訊關係呢?
一種實現方式是在Mediator接口中定義一個特殊的通知接口
,做爲一個通用的方法,讓各個同事類來調用這個方法,在中介者模式結構圖裏畫的就是這種方式。在前面示例的也是這種方式,定義了一個通用的changed方法,而且把同事對象當作參數傳入,這樣在中介者對象裏面,就能夠去獲取這個同事對象的實例的數據了。
另一種實現方式是能夠採用觀察者模式,把Mediator實現成爲觀察者,而各個同事類實現成爲Subject,這樣同事類發生了改變,會通知Mediator
。Mediator在接到通知事後,會與相應的同事對象進行交互。
中介者模式的調用順序如圖10.5所示:
##3.2 廣義中介者## 仔細查看中介者的結構、定義和示例,會發現幾個問題,使得中介者模式在實際使用的時候,變得繁瑣或困難。
你們都知道,Java是單繼承的,爲了使用中介者模式,就讓這些同事對象繼承一個父類,這是很很差的
;再說了,這個父類目前也沒有什麼特別的公共功能,也就是說繼承它也得不到多少好處。
在實際開發中,不少相互交互的對象自己是沒有公共父類的,強行加上一個父類,會讓這些對象實現起來特別彆扭。
同事類須要知道中介者對象,以便當它們發生改變的時候,可以通知中介者對象,可是,是否須要做爲屬性,並經過構造方法傳入,這麼強的依賴關係呢?
也能夠有簡單的方式去通知中介對象,好比把中介對象作成單例,直接在同事類的方法裏面去調用中介者對象。
在實際開發中,很常見的狀況是不須要中介者接口的,並且中介者對象也不須要建立不少個實例,由於中介者是用來封裝和處理同事對象的關係的,它通常是沒有狀態須要維護的,所以中介者一般能夠實現成單例
。
雖然說中介者對象須要知道全部的同事類,這樣中介者才能與它們交互。可是是否須要作爲屬性這麼強烈的依賴關係,並且中介者對象在不一樣的關係維護上,可能會須要不一樣的同事對象的實例
,所以能夠在中介者處理的方法裏面去建立、或者獲取、或者從參數傳入須要的同事對象。
從示例就能夠看出來,在公共方法裏,仍是要去區分究竟是誰調過來,這仍是簡單的,尚未去區分究竟是什麼樣的業務觸發調用過來的,由於不一樣的業務,引發的與其它對象的交互是不同的。
所以在實際開發中,一般會提供具體的業務通知方法,這樣就不用再去判斷究竟是什麼對象,具體是什麼業務了
。
基於上面的考慮,在實際應用開發中,常常會簡化中介者模式,來使開發變得簡單,好比有以下的簡化:
一般會去掉同事對象的父類,這樣可讓任意的對象,只要須要相互交互,就能夠成爲同事;
還有一般不定義Mediator接口,把具體的中介者對象實現成爲單例;
另一點就是同事對象再也不持有中介者,而是在須要的時候直接獲取中介者對象並調用;中介者也再也不持有同事對象,而是在具體處理方法裏面去建立、或者獲取、或者從參數傳入須要的同事對象。
把這樣通過簡化、變形使用的狀況稱爲廣義中介者。仍是來舉個實際點的例子看看吧。
幾乎在每一個應用系統中都須要這樣的功能模塊:部門管理、人員管理,爲了簡單點演示,把模塊簡化成類,也就是有一個部門類Dep和人員類User。
首先想一想部門類Dep和人員類User之間是什麼關係,一對一?一對多?仍是多對多?
可能在不一樣的系統裏面,根據須要會作成不一樣的關係,但從實際狀況講,部門和人員應該是多對多的,也就是一個部門能夠有多我的,而一我的也能夠加入多個部門。
對於一個部門有多我的,估計你們都能理解。而一我的也能夠加入多個部門,或許有些朋友就以爲有些問題了,由於在他們作系統的經驗上,是一我的只屬於一個部門的。
事實上一我的是能夠屬於多個部門的,好比:某人是開發部的經理,同時也是銷售部門的技術總監,爲銷售部門給客戶的解決方案中的技術部分進行把關,同時還能夠是客戶服務部門的技術顧問,爲他們解決客戶的技術問題提供指導。
好了,理解了部門和人員是多對多的關係事後,有些朋友可能會作出以下的設計,不就是個多對多嗎,類之間的多對多也很容易表達啊,以下:
public class Dep { private Collection<User> colUser = new ArrayList<User>(); } public class User { private Collection<Dep> colDep = new ArrayList<Dep>(); }
很簡單,是吧,一個部門有多我的員,一我的員屬於多個部門。
真的這麼簡單嗎?再進一步想一想部門和人員的功能交互,就會知道這樣設計是存在問題的,舉幾個常見的功能:
部門被撤銷
部門之間進行合併
人員離職
人員從一個部門調職到另一個部門
想一想要實現這些功能,按照前面的設計,該怎麼作呢?
(1)系統運行期間,部門被撤銷了,就意味着這個部門不存在了,但是原來這個部門下全部的人員,每一個人員的所屬部門中都有這個部門呢,那麼就須要先通知全部的人員,把這個部門從它們的所屬部門中去掉,而後才能夠清除這個部門。
(2)部門合併,是合併成一個新的部門呢,仍是把一個部門併入到另外一個部門?若是是合併成一個新的部門,那麼須要把原有的兩個部門撤銷,而後再新增一個部門;若是是把一個部門合併到另外一個部門裏面,那就是撤銷掉一個部門,而後把這個部門下的人員移動到這個部門。無論是那種狀況,都面臨着須要通知相應的人員進行更改這樣的問題。
(3)人員離職了,反過來就須要通知他所屬於的部門,從部門的擁有人員的記錄中去除掉這我的員。
(4)人員調職,一樣須要通知相關的部門,先從原來的部門中去除掉,而後再到新的部門中添加上。
看了上述的描述,感受如何?是否是「煩就一個字」啊!
麻煩的根源在什麼地方呢?仔細想一想,對了,麻煩的根源就在於部門和人員之間的耦合,這樣致使操做人員的時候,須要操做全部相關的部門,而操做部門的時候又須要操做全部相關的人員,使得部門和人員攪和在了一塊兒
。
找到了根源就好辦了,採用中介者模式,引入一箇中介者對象來管理部門和人員之間的關係,就能解決這些問題了
。
若是採用標準的中介者模式,想一想上面提出的那些問題點吧,就知道實現起來會很彆扭。所以採用廣義的中介者來解決,這樣部門和人員就徹底解耦了,也就是說部門不知道人員,人員也不知道部門,它們徹底分開,它們之間的關係就徹底由中介者對象來管理了
。這個時候的結構如圖10.6所示:
(1)首先定義部門類,示例代碼以下:
/** * 部門類 */ public class Dep{ /** * 描述部門編號 */ private String depId; /** * 描述部門名稱 */ private String depName; public String getDepId() { return depId; } public void setDepId(String depId) { this.depId = depId; } public String getDepName() { return depName; } public void setDepName(String depName) { this.depName = depName; } /** * 撤銷部門 * @return 是否撤銷成功 */ public boolean deleteDep(){ //1:要先經過中介者去清除掉全部與這個部門相關的部門和人員的關係 DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance(); mediator.deleteDep(depId); //2:而後才能真的清除掉這個部門 //請注意在實際開發中,這些業務功能可能會作到業務層去, //並且實際開發中對於已經使用的業務數據一般是不會被刪除的, //而是會被作爲歷史數據保留 return true; } }
(2)接下來定義人員類,示例代碼以下:
/** * 人員類 */ public class User{ /** * 人員編號 */ private String userId; /** * 人員名稱 */ private String userName; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } /** * 人員離職 * @return 是否處理成功 */ public boolean dimission(){ //1:要先經過中介者去清除掉全部與這我的員相關的部門和人員的關係 DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance(); mediator.deleteUser(userId); //2:而後才能真的清除掉這我的員 //請注意,實際開發中,人員離職,是不會真的刪除人員記錄的, //一般是把人員記錄的狀態或者是刪除標記設置成已刪除, //只是再也不參加新的業務,可是已經發生的業務記錄是不會被清除掉的 return true; } }
(3)順帶看一下描述部門和人員關係的對象,很是簡單,示例代碼以下:
/** * 描述部門和人員關係的類 */ public class DepUserModel { /** * 用於部門和人員關係的編號,用作主鍵 */ private String depUserId; /** * 部門的編號 */ private String depId; /** * 人員的編號 */ private String userId; }
(4)具體的中介者實現:
首先中介者要管理部門和人員的關係
,因此在中介者實現裏面添加了一些測試的數據,爲此還專門作了一個用來描述部門和人員關係的數據對象;其次在中介者裏面只是實現了撤銷部門和人員離職相應的關係處理,其它的沒有實現
;另外,這個中介者實現被實現成單例的了。示例代碼以下:
/** * 實現部門和人員交互的中介者實現類 * 說明:爲了演示的簡潔性,只示例實現撤銷部門和人員離職的功能 */ public class DepUserMediatorImpl{ private static DepUserMediatorImpl mediator = new DepUserMediatorImpl(); private DepUserMediatorImpl(){ //調用初始化測試數據的功能 initTestData(); } public static DepUserMediatorImpl getInstance(){ return mediator; } /** * 測試用,記錄部門和人員的關係 */ private Collection<DepUserModel> depUserCol = new ArrayList<DepUserModel>(); /** * 初始化測試數據 */ private void initTestData(){ //準備一些測試數據 DepUserModel du1 = new DepUserModel(); du1.setDepUserId("du1"); du1.setDepId("d1"); du1.setUserId("u1"); depUserCol.add(du1); DepUserModel du2 = new DepUserModel(); du2.setDepUserId("du2"); du2.setDepId("d1"); du2.setUserId("u2"); depUserCol.add(du2); DepUserModel du3 = new DepUserModel(); du3.setDepUserId("du3"); du3.setDepId("d2"); du3.setUserId("u3"); depUserCol.add(du3); DepUserModel du4 = new DepUserModel(); du4.setDepUserId("du4"); du4.setDepId("d2"); du4.setUserId("u4"); depUserCol.add(du4); DepUserModel du5 = new DepUserModel(); du5.setDepUserId("du5"); du5.setDepId("d2"); du5.setUserId("u1"); depUserCol.add(du5); } /** * 完成因撤銷部門的操做所引發的與人員的交互,須要去除相應的關係 * @param depId 被撤銷的部門對象的編號 * @return 是否已經正確的處理了因撤銷部門所引發的與人員的交互 */ public boolean deleteDep(String depId) { //請注意:爲了演示簡單,部門撤銷後, //原部門的人員怎麼處理等後續業務處理,這裏就無論了 //1:到記錄部門和人員關係的集合裏面,尋找跟這個部門相關的人員 //設置一個臨時的集合,記錄須要清除的關係對象 Collection<DepUserModel> tempCol = new ArrayList<DepUserModel>(); for(DepUserModel du : depUserCol){ if(du.getDepId().equals(depId)){ //2:須要把這個相關的記錄去掉,先記錄下來 tempCol.add(du); } } //3:從關係集合裏面清除掉這些關係 depUserCol.removeAll(tempCol); return true; } /** * 完成因人員離職引發的與部門的交互 * @param userId 離職的人員的編號 * @return 是否正確處理了因人員離職引發的與部門的交互 */ public boolean deleteUser(String userId) { //1:到記錄部門和人員關係的集合裏面,尋找跟這我的員相關的部門 //設置一個臨時的集合,記錄須要清除的關係對象 Collection<DepUserModel> tempCol = new ArrayList<DepUserModel>(); for(DepUserModel du : depUserCol){ if(du.getUserId().equals(userId)){ //2:須要把這個相關的記錄去掉,先記錄下來 tempCol.add(du); } } //3:從關係集合裏面清除掉這些關係 depUserCol.removeAll(tempCol); return true; } /** * 測試用,在內部打印顯示一下一個部門下的全部人員 * @param dep 部門對象 */ public void showDepUsers(Dep dep) { for(DepUserModel du : depUserCol){ if(du.getDepId().equals(dep.getDepId())){ System.out.println("部門編號="+dep.getDepId()+"下面擁有人員,其編號是:"+du.getUserId()); } } } /** * 測試用,在內部打印顯示一下一我的員所屬的部門 * @param user 人員對象 */ public void showUserDeps(User user) { for(DepUserModel du : depUserCol){ if(du.getUserId().equals(user.getUserId())){ System.out.println("人員編號="+user.getUserId()+"屬於部門編號是:"+du.getDepId()); } } } /** * 完成因人員調換部門引發的與部門的交互 * @param userId 被調換的人員的編號 * @param oldDepId 調換前的部門的編號 * @param newDepId 調換後的部門的編號 * @return 是否正確處理了因人員調換部門引發的與部門的交互 */ public boolean changeDep(String userId,String oldDepId, String newDepId) { //本示例不去實現了 return false; } /** * 完成因部門合併操做所引發的與人員的交互 * @param colDepIds 須要合併的部門的編號集合 * @param newDep 合併後新的部門對象 * @return 是否正確處理了因部門合併操做所引發的與人員的交互 */ public boolean joinDep(Collection<String> colDepIds, Dep newDep){ //本示例不去實現了 return false; } }
(5)測試一下,看看好用不,客戶端示例代碼以下:
public class Client { public static void main(String[] args) { DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance(); //準備要撤銷的部門,僅僅須要一個部門編號 Dep dep = new Dep(); dep.setDepId("d1"); Dep dep2 = new Dep(); dep2.setDepId("d2"); //準備用於測試的人員,也只須要一我的員編號 User user = new User(); user.setUserId("u1"); //測試撤銷部門,在運行以前,輸出一下,看這我的員屬於哪些部門 System.out.println("撤銷部門前------------------"); mediator.showUserDeps(user); //真正執行業務,撤銷這個部門 dep.deleteDep(); //再次輸出一下,看這我的員屬於哪些部門 System.out.println("撤銷部門後------------------"); mediator.showUserDeps(user); //測試人員離職,在運行以前,輸出一下,看這個部門下都有哪些人員 System.out.println("---------------------------------"); System.out.println("人員離職前------------------"); mediator.showDepUsers(dep2); //真正執行業務,人員離職 user.dimission(); //再次輸出一下,看這個部門下都有哪些人員 System.out.println("人員離職後------------------"); mediator.showDepUsers(dep2); } }
運行結果以下:
撤銷部門前------------------ 人員編號=u1屬於部門編號是:d1 人員編號=u1屬於部門編號是:d2 撤銷部門後------------------ 人員編號=u1屬於部門編號是:d2 --------------------------------- 人員離職前------------------ 部門編號=d2下面擁有人員,其編號是:u3 部門編號=d2下面擁有人員,其編號是:u4 部門編號=d2下面擁有人員,其編號是:u1 人員離職後------------------ 部門編號=d2下面擁有人員,其編號是:u3 部門編號=d2下面擁有人員,其編號是:u4
好好體會一下,看看這樣作是否是變得更容易了些,並且也實現了中介者想要實現的功能,那就是讓同事對象相互分離,由中介對象統一管理它們的交互。
##3.3 中介者模式的優缺點##
中介者模式經過把多個同事對象之間的交互封裝到中介者對象裏面,從而使得同事對象之間鬆散耦合,基本上能夠作到互不依賴。這樣一來,同事對象就能夠獨立的變化和複用,而再也不像之前那樣「牽一髮而動全身」了。
多個同事對象的交互,被封裝在中介者對象裏面集中管理,使得這些交互行爲發生變化的時候,只須要修改中介者對象就能夠了,固然若是是已經作好的系統,那就擴展中介者對象,而各個同事類不須要作修改。
沒有使用中介者模式的時候,同事對象之間的關係一般是多對多的,引入中介者對象事後,中介者對象和同事對象的關係一般變成了雙向的一對多,這會讓對象的關係更容易理解和實現。
中介者模式的一個潛在缺點是,若是同事對象的交互很是多,並且比較複雜,當這些複雜性所有集中到中介者的時候,會致使中介者對象變得十分的複雜,並且難於管理和維護
。
##3.4 思考中介者模式##
中介者模式的本質:封裝交互。
中介者模式的目的,就是用來封裝多個對象的交互,這些交互的處理多在中介者對象裏面實現,所以中介對象的複雜程度,就取決於它封裝的交互有多複雜了。
只要是實現封裝對象之間的交互功能,就能夠應用上中介者模式,而沒必要過於拘泥於中介者模式自己的結構
。標準的中介者模式限制不少,致使能徹底按照標準使用中介者模式的地方並非不少,並且多集中在界面實現上。只要本質不變,稍稍變形一下,簡化一下,或許能更好的使用中介者模式
。
建議在以下狀況中,選用中介者模式:
若是一組對象之間的通訊方式比較複雜,致使相互依賴、結構混亂,能夠採用中介者模式,把這些對象相互的交互管理起來,各個對象都只須要和中介者交互,從而使得各個對象鬆散耦合,結構也更清晰易懂。
若是一個對象引用不少的對象,並直接跟這些對象交互,致使難以複用該對象。能夠採用中介者模式,把這個對象跟其它對象的交互封裝到中介者對象裏面,這個對象就只須要和中介者對象交互就能夠了。
##3.5 相關模式##
這兩個模式有類似的地方,也存在很大的不一樣。
外觀模式多用來封裝一個子系統內部的多個模塊,目的是向子系統外部提供簡單易用的接口
,也就是說外觀模式封裝的是子系統外部和子系統內部模塊間的交互;而中介者模式是提供多個平等的同事對象之間交互關係的封裝,通常是用在內部實現上
。
另外,外觀模式是實現單向的交互,是從子系統外部來調用子系統內部,不會反着來,而中介者模式實現的是內部多個模塊間多向的交互
。
這兩個模式能夠組合使用。
中介者模式能夠組合使用觀察者模式,來實現當同事對象發生改變的時候,通知中介對象,讓中介對象去進行與其它相關對象的交互
。