中介者模式 標簽 : 設計模式 初識中介者模式 定義 用一個中介對象來封裝一系列的對象交互。中介者使得各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立的改變它們之間的交互。 結構和說明 ![image_1cichf9j215a4eatf87cma7sm9.png 86.7kB][1] Me ...
中介者模式
標簽 : 設計模式
初識中介者模式
定義
用一個中介對象來封裝一系列的對象交互。中介者使得各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立的改變它們之間的交互。
結構和說明
Mediator:中介者介面。在裡面定義各個同事之間交互需要的方法,可以是公共的通訊方法,比如changed方法,大家都用,也可以是小範圍的交互方法。
ConcreteMediator:具體中介者實現對象。它需要瞭解並維護各個同事對象,並負責具體的協調各同事對象的交互關係。
Colleague:同事類的定義,通常實現成為抽象類,主要負責約束同事對象的類型,並實現一些具體同事類之間的公共功能,比如:每個具體同事類都應該知道中介者對象,也就是具體同事類都會持有中介者對象,就可以定義到這個類裡面。
ConcreteColleague:具體的同事類,實現自己的業務,在需要與其它同事通訊的時候,就與持有的中介者通信,中介者會負責與其它的同事交互。
/**
* 同事類的抽象父類
*/
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) {
//某個同事類發生了變化,通常需要與其他同事交戶
//具體協調相應的同事對象來實現協作行為
}
}
體會中介者模式
如果電腦裡面沒有了主板,那麼各個配件之間就必須自行相互交互,以互相傳送數據,理論上說,基本上各個配件相互之間都存在交互數據的可能
有了主板,各個配件的交互完全通過主板來完成,每個配件都只需要和主板交互,而主板知道如何和所有的配件打交道,那就簡單多了
存在的問題
如果上面的情況發生在軟體開發上呢?
如果把每個電腦配件都抽象成為一個類或者是子系統,那就相當於出現了多個類之間相互交互,而且交互還很繁瑣,導致每個類都必須知道所有需要交互的類,也就是我們常說的類和類耦合了,是不是很麻煩?
這樣一來,不但開發的時候每個類會複雜,因為要兼顧其它的類,更要命的是每個類在發生改動的時候,需要通知所有相關的類一起修改,因為介面或者是功能發生了變動,使用它的地方都得變,太可怕了吧!
那該如何來簡化這種多個對象之間的交互呢?
使用電腦來看電影
為了演示,考慮一個稍微具體點的功能。在日常生活中,我們經常使用電腦來看電影,把這個過程描述出來,這裡僅僅考慮正常的情況,也就是有主板的情況,簡化後假定會有如下的交互過程:
1:首先是光碟機要讀取光碟上的數據,然後告訴主板,它的狀態改變了
2:主板去得到光碟機的數據,把這些數據交給CPU進行分析處理
3:CPU處理完後,把數據分成了視頻數據和音頻數據,通知主板,它處理完了
4:主板去得到CPU處理過後的數據,分別把數據交給顯卡和音效卡,去顯示出視頻和發出聲音
當然這是一個持續的、不斷重覆的過程,從而形成不間斷的視頻和聲音,具體的運行過程不在討論之列,假設就有如上簡單的交互關係就可以了。也就是說想看電影,把光碟放入光碟機,光碟機開始讀盤,就可以看電影了
使用模式的解決方案
/**
* 同事類的抽象父類
*/
public abstract class Colleague {
/**
* 持有中介者對象,每一個同事類都知道它的中介者對象
*/
private Mediator mediator;
/**
* 構造方法,傳入中介者對象
* @param mediator 中介者對象
*/
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
/**
* 獲取當前同事類對應的中介者對象
* @return 對應的中介者對象
*/
public Mediator getMediator() {
return mediator;
}
}
----------
/**
* 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 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);
}
}
----------
/**
* 音效卡類,一個同事類
*/
public class SoundCard extends Colleague{
public SoundCard(Mediator mediator) {
super(mediator);
}
/**
* 按照聲頻數據發出聲音
* @param data 發出聲音的數據
*/
public void soundData(String data){
System.out.println("畫外音:"+data);
}
}
----------
/**
* 顯卡類,一個同事類
*/
public class VideoCard extends Colleague{
public VideoCard(Mediator mediator) {
super(mediator);
}
/**
* 顯示視頻數據
* @param data 被顯示的數據
*/
public void showData(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();
}
}
理解中介者模式
認識中介者模式
1:中介者模式的功能
中介者的功能非常簡單,就是封裝對象之間的交互。如果一個對象的操作會引起其它相關對象的變化,或者是某個操作需要引起其它對象的後續或連帶操作,而這個對象又不希望自己來處理這些關係,那麼就可以找中介者,把所有的麻煩扔給它,只在需要的時候通知中介者,其它的就讓中介者去處理就可以了。
反過來,其它的對象在操作的時候,可能會引起這個對象的變化,也可以這麼做。最後對象之間就完全分離了,誰都不直接跟其它對象交互,那麼相互的關係,全部被集中到中介者對象裡面了,所有的對象就只是跟中介者對象進行通信,相互之間不再有聯繫。
把所有對象之間的交互都封裝在中介者當中,無形中還得到另外一個好處,就是能夠集中的控制這些對象的交互關係,這樣有什麼變化的時候,修改起來就很方便。
2:需要Mediator介面嗎
有沒有使用Mediator介面的必要,取決於是否會提供多個不同的中介者實現。如果中介者實現只有一個的話,而且預計中也沒有需要擴展的要求,那麼就可以不定義Mediator介面,讓各個同事對象直接使用中介者實現對象;如果中介者實現不只一個,或者預計中有擴展的要求,那麼就需要定義Mediator介面,讓各個同事對象來面向中介者介面編程,而無需關心具體的中介者實現。
3:同事關係
在中介者模式中,要求這些類都要繼承相同的類,也就是說,這些對象從某個角度講是同一個類型,算是兄弟對象。
正是這些兄弟對象之間的交互關係很複雜,才產生了把這些交互關係分離出去,單獨做成中介者對象,這樣一來,這些兄弟對象就成了中介者對象眼裡的同事。
4:同事和中介者的關係
中介者對象和同事對象之間是相互依賴的 。
5:如何實現同事和中介者的通信
一種實現方式是在Mediator介面中定義一個特殊的通知介面,作為一個通用的方法,讓各個同事類來調用這個方法 。
另外一種實現方式是可以採用觀察者模式,把Mediator實現成為觀察者,而各個同事類實現成為Subject,這樣同事類發生了改變,會通知Mediator。Mediator在接到通知過後,會與相應的同事對象進行交互。
6:中介者模式的調用順序示意圖
廣義中介者示例——部門與人員
1:部門和人員的關係 : 是 多對多的
2:問題的出現
想想部門和人員的功能交互,舉幾個常見的功能:
(1)部門被撤銷
(2)部門之間進行合併
(3)人員離職
(4)人員從一個部門調職到另外一個部門
想想要實現這些功能,按照前面的設計,該怎麼做呢?
(1)系統運行期間,部門被撤銷了,就意味著這個部門不存在了,可是原來這個部門下所有的人員,每個人員的所屬部門中都有這個部門呢,那麼就需要先通知所有的人員,把這個部門從它們的所屬部門中去掉,然後才可以清除這個部門。
(2)部門合併,是合併成一個新的部門呢,還是把一個部門併入到另一個部門?如果是合併成一個新的部門,那麼需要把原有的兩個部門撤銷,然後再新增一個部門;如果是把一個部門合併到另一個部門裡面,那就是撤銷掉一個部門,然後把這個部門下的人員移動到這個部門。不管是那種情況,都面臨著需要通知相應的人員進行更改這樣的問題。
(3)人員離職了,反過來就需要通知他所屬於的部門,從部門的擁有人員的記錄中去除掉這個人員。
(4)人員調職,同樣需要通知相關的部門,先從原來的部門中去除掉,然後再到新的部門中添加上。
看了上述的描述,感覺如何?
麻煩的根源在什麼地方呢?仔細想想,對了,麻煩的根源就在於部門和人員之間的耦合,這樣導致操作人員的時候,需要操作所有相關的部門,而操作部門的時候又需要操作所有相關的人員,使得部門和人員攪和在了一起。
3:中介者來解決
找到了根源就好辦了,採用中介者模式,引入一個中介者對象來管理部門和人員之間的關係,就能解決這些問題了。
如果採用標準的中介者模式,想想上面提出的那些問題點吧,就知道實現起來會很彆扭。因此採用廣義的中介者來解決,這樣部門和人員就完全解耦了,也就是說部門不知道人員,人員也不知道部門,它們完全分開,它們之間的關係就完全由中介者對象來管理了。
/**
* 描述部門和人員關係的類
*/
public class DepUserModel {
/**
* 用於部門和人員關係的編號,用做主鍵
*/
private String depUserId;
/**
* 部門的編號
*/
private String depId;
/**
* 人員的編號
*/
private String userId;
public String getDepUserId() {
return depUserId;
}
public void setDepUserId(String depUserId) {
this.depUserId = depUserId;
}
public String getDepId() {
return depId;
}
public void setDepId(String depId) {
this.depId = depId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
----------
/**
* 部門類
*/
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;
}
}
----------
/**
* 人員名稱
*/
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;
}
}
----------
/**
* 實現部門和人員交互的中介者實現類
* 說明:為了演示的簡潔性,只示例實現撤銷部門和人員離職的功能
*/
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;
}
}
----------
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);
}
}
中介者模式的優缺點
1:鬆散耦合
2:集中控制交互
3:多對多變成一對多
4:過度集中化
思考中介者模式
中介者模式的本質
封裝交互
何時選用中介者模式
1:如果一組對象之間的通信方式比較複雜,導致相互依賴、結構混亂,可以採用中介者模式,把這些對象相互的交互管理起來,各個對象都只需要和中介者交互,從而使得各個對象鬆散耦合,結構也更清晰易懂
2:如果一個對象引用很多的對象,並直接跟這些對象交互,導致難以復用該對象。可以採用中介者模式,把這個對象跟其它對象的交互封裝到中介者對象裡面,這個對象就只需要和中介者對象交互就可以了