目錄
本文的結(jié)構(gòu)如下:
- 引言
- 什么是中介者模式
- 模式的結(jié)構(gòu)
- 典型代碼
- 代碼示例
- 優(yōu)點(diǎn)和缺點(diǎn)
- 適用環(huán)境
- 模式應(yīng)用
一、引言
日常開(kāi)發(fā)是離不開(kāi)電腦的,這需要cpu、內(nèi)存、顯卡、鍵盤(pán)、顯示器等零件相互調(diào)用,如果直接讓這些零件互相調(diào)用,它們之間的關(guān)系會(huì)很復(fù)雜:

顯然這樣造成的后果是難以維護(hù),為了避免這種情況,開(kāi)發(fā)商引入了主板,由主板和各部件進(jìn)行交互,統(tǒng)一協(xié)調(diào),這樣每個(gè)部件只需要把命令傳給主板,由主板決定同哪個(gè)部件交互,然后接受主板返回的數(shù)據(jù)即可,不需要知道其他部件的存在:

在軟件開(kāi)發(fā)中,面向?qū)ο笤O(shè)計(jì)鼓勵(lì)將行為分布到各個(gè)對(duì)象中。這種分布可能會(huì)導(dǎo)致對(duì)象間有許多連接。在最壞的情況下,每一個(gè)對(duì)象都知道其他所有對(duì)象。
將一個(gè)系統(tǒng)分割成許多對(duì)象通??梢栽鰪?qiáng)可復(fù)用性,但是對(duì)象間相互連接的激增又會(huì)降低其可復(fù)用性。大量的相互連接使得一個(gè)對(duì)象似乎不太可能在沒(méi)有其他對(duì)象的支持下工作—--系統(tǒng)表現(xiàn)為一個(gè)不可分割的整體(過(guò)度耦合)。而且對(duì)系統(tǒng)的行為進(jìn)行任何較大的改動(dòng)都十分困難。
這時(shí)可以像電腦開(kāi)發(fā)商一樣引入“主板”來(lái)解決,這就是中介者模式。
二、什么是中介者模式
如果在一個(gè)系統(tǒng)中對(duì)象之間存在多對(duì)多的相互關(guān)系(像沒(méi)有引入主板前電腦各部件,它們被稱(chēng)為“同事類(lèi)”),這時(shí)可以將對(duì)象之間的一些交互行為從各個(gè)對(duì)象中分離出來(lái),并集中封裝在一個(gè)中介者對(duì)象中,并由該中介者進(jìn)行統(tǒng)一協(xié)調(diào),這樣對(duì)象之間多對(duì)多的復(fù)雜關(guān)系就轉(zhuǎn)化為相對(duì)簡(jiǎn)單的一對(duì)多關(guān)系。通過(guò)引入中介者來(lái)簡(jiǎn)化對(duì)象之間的復(fù)雜交互,中介者模式是“迪米特法則”的一個(gè)典型應(yīng)用。
中介者模式定義如下:
中介者模式(Mediator Pattern):用一個(gè)中介對(duì)象(中介者)來(lái)封裝一系列的對(duì)象交互,中介者使各對(duì)象不需要顯式地相互引用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互。中介者模式又稱(chēng)為調(diào)停者模式,它是一種對(duì)象行為型模式。
中介者模式解決的困境就是多個(gè)對(duì)象之間的相互引用導(dǎo)致的緊耦合,通過(guò)引入一個(gè)中介者,原來(lái)互相引用的對(duì)象就相互解耦了,他們之間現(xiàn)在沒(méi)有任何關(guān)系,只和中介者交互。
說(shuō)白了中介者模式把對(duì)象之間的多對(duì)多關(guān)系轉(zhuǎn)換成了同中介者類(lèi)的一對(duì)多關(guān)系,從而降低耦合。
三、模式的結(jié)構(gòu)
中介者模式UML類(lèi)圖如下:

中介者模式將一個(gè)網(wǎng)狀的系統(tǒng)結(jié)構(gòu)變成一個(gè)以中介者對(duì)象為中心的星形結(jié)構(gòu),在這個(gè)星型結(jié)構(gòu)中,使用中介者對(duì)象與其他對(duì)象的一對(duì)多關(guān)系來(lái)取代原有對(duì)象之間的多對(duì)多關(guān)系。在中介者模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
- Mediator(抽象中介者):中介者接口,該接口用于與各同事對(duì)象之間進(jìn)行通信。
- ConcreteMediator(具體中介者):具體中介者實(shí)現(xiàn)對(duì)象,它維持對(duì)各個(gè)同事對(duì)象的引用,協(xié)調(diào)各個(gè)同事對(duì)象來(lái)實(shí)現(xiàn)協(xié)作行為。
- Colleague(抽象同事類(lèi)):主要負(fù)責(zé)約束同事對(duì)象的類(lèi)型,它定義各個(gè)同事類(lèi)公有的方法,并聲明了一些抽象方法來(lái)供子類(lèi)實(shí)現(xiàn),同時(shí)它維持了一個(gè)對(duì)抽象中介者類(lèi)的引用,其子類(lèi)可以通過(guò)該引用來(lái)與中介者通信。
- ConcreteColleague(具體同事類(lèi)):在需要與其他同事通信的時(shí)候,就與持有的中介者通信,中介者負(fù)責(zé)與其他的同事進(jìn)行交互;在具體同事類(lèi)中實(shí)現(xiàn)了在抽象同事類(lèi)中聲明的抽象方法。
中介者模式的核心是中介者類(lèi),它承擔(dān)了兩方面的職責(zé):
- 中轉(zhuǎn)作用(結(jié)構(gòu)性):通過(guò)中介者提供的中轉(zhuǎn)作用,各個(gè)同事對(duì)象就不再需要顯式引用其他同事,當(dāng)需要和其他同事進(jìn)行通信時(shí),可通過(guò)中介者來(lái)實(shí)現(xiàn)間接調(diào)用。該中轉(zhuǎn)作用屬于中介者在結(jié)構(gòu)上的支持。
- 協(xié)調(diào)作用(行為性):中介者可以更進(jìn)一步的對(duì)同事之間的關(guān)系進(jìn)行封裝,同事可以一致的和中介者進(jìn)行交互,而不需要指明中介者需要具體怎么做,中介者根據(jù)封裝在自身內(nèi)部的協(xié)調(diào)邏輯,對(duì)同事的請(qǐng)求進(jìn)行進(jìn)一步處理,將同事成員之間的關(guān)系行為進(jìn)行分離和封裝。該協(xié)調(diào)作用屬于中介者在行為上的支持。
四、典型代碼
抽象中介者類(lèi)典型代碼如下:
public abstract class Mediator {
public abstract void Send(String message, Colleague colleague);
}
具體中介者典型代碼如下:
public class ConcreteMediator extends Mediator{
private ConcreteColleague1 colleague1;
private ConcreteColleague2 colleague2;
public void setColleague1(ConcreteColleague1 colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(ConcreteColleague2 colleague2) {
this.colleague2 = colleague2;
}
public void Send(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.Notify(message);
} else if (colleague == colleague2){
colleague1.Notify(message);
} else {
System.out.println("Error!");
}
}
}
抽象同事類(lèi)典型代碼:
public abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public void Send(String message) {
mediator.Send(message, this);
}
public abstract void Notify(String message);
}
具體同事類(lèi)典型代碼:
public class ConcreteColleague1 extends Colleague{
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
public void Notify(String message) {
System.out.println("同事1得到信息:" + message);
}
}
public class ConcreteColleague2 extends Colleague{
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
public void Notify(String message) {
System.out.println("同事2得到信息:" + message);
}
}
客戶(hù)端測(cè)試:
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.Send("How are you?");
colleague2.Send("Fine, thank you. And you?");
colleague1.Send("I'm fine. Thankes.");
}
}
五、代碼示例
5.1、標(biāo)準(zhǔn)中介者模式
假設(shè)使用電腦播放視頻,把步驟分為如下幾步:
- 光驅(qū)讀取光盤(pán)內(nèi)容,把讀取到的內(nèi)容傳遞給主板。
- 主板得到內(nèi)容,交給cpu處理。
- cpu處理完畢,把處理后的數(shù)據(jù)傳遞給主板。
- 主板把數(shù)據(jù)傳遞給顯卡,顯卡顯示視頻(忽略了顯示器顯示這個(gè)步驟)。
不使用中介者模式
public class CDDriver{
private String data;
private CPU cpu;
public CDDriver(){
this.cpu = new CPU();
}
public String getData() {
return data;
}
public void readCD(){
//逗號(hào)前是視頻數(shù)據(jù),逗號(hào)后是音頻數(shù)據(jù)
this.data = "Video Data,Sound Data";
//然后CPU處理
cpu.executeData(data);
}
}
public class CPU{
private VideoCard videoCard;
private SoundCard soundCard;
public CPU() {
videoCard = new VideoCard();
soundCard = new SoundCard();
}
public void executeData(String data){
//分解數(shù)據(jù),前面是視頻數(shù)據(jù),后面是音頻數(shù)據(jù)
String[] ss = data.split(",");
String videoData = ss[0];
String soundData = ss[1];
videoCard.showData(videoData);
soundCard.soundData(soundData);
}
}
public class VideoCard{
public void showData(String data){
System.out.println("你正在觀看的是:" + data);
}
}
public class SoundCard{
public void soundData(String data){
System.out.println("你聽(tīng)到的聲音是:" + data);
}
}

這里設(shè)計(jì)的關(guān)聯(lián)其實(shí)還算簡(jiǎn)單,并沒(méi)有很復(fù)雜,但仍然會(huì)發(fā)現(xiàn)有以下問(wèn)題:
- 系統(tǒng)耦合度高:每組件與多個(gè)其他組件之間產(chǎn)生相互關(guān)聯(lián)和調(diào)用,若一個(gè)組件對(duì)象發(fā)生變化,需要跟蹤與之有關(guān)聯(lián)的其他所有組件并進(jìn)行處理,組件之間的耦合度高。
- 組件的可重用性差:由于每一個(gè)組件和其他組件之間都具有很強(qiáng)的關(guān)聯(lián),若沒(méi)有其他組件的支持,一個(gè)組件很難被另一個(gè)系統(tǒng)或模塊重用,這些組件表現(xiàn)出來(lái)更像一個(gè)不可分割的整體,而在實(shí)際使用時(shí),我們往往需要每一個(gè)組件都能夠單獨(dú)重用,而不是重用一個(gè)由多個(gè)組件組成的復(fù)雜結(jié)構(gòu)。
- 系統(tǒng)的可擴(kuò)展性差:如果在上述系統(tǒng)中增加一個(gè)新的組件類(lèi),則必須修改與之交互的其他組件類(lèi)的源代碼,將導(dǎo)致多個(gè)類(lèi)的源代碼需要修改,同樣,如果要?jiǎng)h除一個(gè)組件也存在類(lèi)似的問(wèn)題,這違反了“開(kāi)閉原則”,可擴(kuò)展性和靈活性欠佳。
使用中介者模式
抽象中介者:
public interface Mediator {
void changed(Colleague colleague);
}
具體中介者主板類(lèi):
public class MainBoard implements Mediator {
private CDDriver cdDriver;
private CPU cpu;
private VideoCard videoCard;
private SoundCard soundCard;
public void changed(Colleague colleague) {
if(colleague == cdDriver){
//表示光驅(qū)讀取了數(shù)據(jù)
this.operateCDDriverReadData((CDDriver)colleague);
}else if(colleague == cpu){
//表示CPU處理完數(shù)據(jù)
this.operateCPU((CPU)colleague);
}
}
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 operateCDDriverReadData(CDDriver cd){
String data = cd.getData();
this.cpu.executeData(data);
}
public void operateCPU(CPU cpu){
String videoData = cpu.getVideoData();
String soundData = cpu.getSoundData();
this.videoCard.showData(videoData);
this.soundCard.soundData(soundData);
}
}
抽象同事類(lèi):
public abstract class Colleague {
private final Mediator mediator;
public Colleague(Mediator mediator){
this.mediator = mediator;
}
public Mediator getMediator(){
return mediator;
}
}
具體同事類(lèi):
public class CDDriver extends Colleague {
private String data;
public CDDriver(Mediator mediator) {
super(mediator);
}
public String getData() {
return data;
}
public void readCD(){
//逗號(hào)前是視頻數(shù)據(jù),逗號(hào)后是音頻數(shù)據(jù)
this.data = "Video Data,Sound Data";
//通知主板,自己的狀態(tài)發(fā)生了變化
this.getMediator().changed(this);
}
}
public class CPU extends Colleague {
private String videoData;
private String soundData;
public CPU(Mediator mediator) {
super(mediator);
}
public String getVideoData() {
return videoData;
}
public String getSoundData() {
return soundData;
}
public void executeData(String data){
//分解數(shù)據(jù),前面是視頻數(shù)據(jù),后面是音頻數(shù)據(jù)
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);
}
public void showData(String data){
System.out.println("你正在觀看的是:" + data);
}
}
public class SoundCard extends Colleague {
public SoundCard(Mediator mediator) {
super(mediator);
}
public void soundData(String data){
System.out.println("你聽(tīng)到的聲音是:" + data);
}
}
客戶(hù)端測(cè)試:
public class MediatorPatternDemo {
public static void main(String[] args) {
MainBoard mediator = new MainBoard();
CDDriver cd = new CDDriver(mediator);
CPU cpu = new CPU(mediator);
VideoCard vc = new VideoCard(mediator);
SoundCard sc = new SoundCard(mediator);
mediator.setCdDriver(cd);
mediator.setCpu(cpu);
mediator.setVideoCard(vc);
mediator.setSoundCard(sc);
cd.readCD();
}
}

引入中介者類(lèi)后,再來(lái)看就會(huì)發(fā)現(xiàn)組件之間不再相互關(guān)聯(lián),系統(tǒng)之間耦合度降低,更易復(fù)用,擴(kuò)展和維護(hù),當(dāng)然會(huì)發(fā)現(xiàn)系統(tǒng)相關(guān)變得復(fù)雜。
5.2、更廣義的中介者模式
我們知道現(xiàn)實(shí)開(kāi)發(fā)中,很多時(shí)候不會(huì)完全符合標(biāo)準(zhǔn)中介者模式,這時(shí)可以實(shí)際情況做一些變化,比如:
- 去掉同事對(duì)象的父類(lèi),這樣可以讓任意的對(duì)象,只要需要相互交互,就可以成為同事。
- 不定義Mediator接口,把具體的中介者實(shí)現(xiàn)成單例。
- 同事對(duì)象不再持有中介者對(duì)象,而是在具體處理方法里面去創(chuàng)建或者獲取,或者從參數(shù)傳入需要的同事對(duì)象。
現(xiàn)假設(shè)有有小說(shuō)類(lèi)和類(lèi)別類(lèi),為此制定了一個(gè)書(shū)單,書(shū)單上有平時(shí)追更的小說(shuō),當(dāng)小說(shuō)完結(jié)了,也就追更完畢,就把小說(shuō)從書(shū)單刪除;還有可以將冷門(mén)類(lèi)別的小說(shuō)從書(shū)單刪除......
這樣實(shí)現(xiàn):
public class Novel {
private String novelId;
private String novelName;
private boolean finsh;
public Novel(String novelId, String novelName){
this.novelId = novelId;
this.novelName = novelName;
}
public String getNovelId() {
return novelId;
}
public String getNovelName() {
return novelName;
}
public void setNovelId(String novelId) {
this.novelId = novelId;
}
public void setNovelName(String novelName) {
this.novelName = novelName;
}
public boolean isFinsh() {
return finsh;
}
public void setFinsh(boolean finsh) {
//如果完結(jié)了,就從書(shū)單刪除
if (finsh){
NovelTypeMediator mediator = NovelTypeMediator.getInstance();
mediator.removeNovel(novelId);
}
this.finsh = finsh;
}
}
public class Type {
private String typeId;
private String typeName;
private boolean unpopular;
public Type(String typeId, String typeName) {
this.typeId = typeId;
this.typeName = typeName;
}
public void setTypeId(String typeId) {
this.typeId = typeId;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getTypeId() {
return typeId;
}
public String getTypeName() {
return typeName;
}
public boolean isUnpopular() {
return unpopular;
}
public void setUnpopular(boolean unpopular) {
if (unpopular){
NovelTypeMediator mediator = NovelTypeMediator.getInstance();
mediator.removeType(typeId);
}
this.unpopular = unpopular;
}
}
public class NovelTypeRelation {
private String id;
private String novelId;
private String typeId;
public NovelTypeRelation(String id, String novelId, String typeId) {
this.id = id;
this.novelId = novelId;
this.typeId = typeId;
}
public String getId() {
return id;
}
public String getNovelId() {
return novelId;
}
public String getTypeId() {
return typeId;
}
public void setId(String id) {
this.id = id;
}
public void setNovelId(String novelId) {
this.novelId = novelId;
}
public void setTypeId(String typeId) {
this.typeId = typeId;
}
}
public class NovelTypeMediator {
private static NovelTypeMediator instance = new NovelTypeMediator();
//書(shū)單
private List<NovelTypeRelation> novelList = new ArrayList<NovelTypeRelation>();
public static NovelTypeMediator getInstance(){
return instance;
}
private NovelTypeMediator(){
initData();
}
private void initData() {
NovelTypeRelation n1 = new NovelTypeRelation("1", "1n", "1t");
NovelTypeRelation n2 = new NovelTypeRelation("2", "2n", "1t");
NovelTypeRelation n3 = new NovelTypeRelation("3", "3n", "2t");
NovelTypeRelation n4 = new NovelTypeRelation("4", "4n", "2t");
novelList.add(n1);
novelList.add(n2);
novelList.add(n3);
novelList.add(n4);
}
/**
* 展示書(shū)單
*/
public void showNovelList(){
for (NovelTypeRelation relation : novelList){
System.out.println("小說(shuō)編號(hào)是:" + relation.getNovelId() + ", 類(lèi)型編號(hào)是:" + relation.getTypeId());
}
}
/**
* 刪除小說(shuō)
* @param novelId
*/
public void removeNovel(String novelId){
for (Iterator<NovelTypeRelation> itr = novelList.iterator(); itr.hasNext();){
NovelTypeRelation relation = itr.next();
if (relation.getNovelId().equals(novelId)){
itr.remove();
}
}
}
/**
* 刪除類(lèi)型
* @param typeId
*/
public void removeType(String typeId){
for (Iterator<NovelTypeRelation> itr = novelList.iterator(); itr.hasNext();){
NovelTypeRelation relation = itr.next();
if (relation.getTypeId().equals(typeId)){
itr.remove();
}
}
}
}
測(cè)試類(lèi):
public class Client {
public static void main(String[] args) {
NovelTypeMediator mediator = NovelTypeMediator.getInstance();
//展示書(shū)單
mediator.showNovelList();
//完結(jié)
Novel novel = new Novel("1n", "誅仙");
novel.setFinsh(true);
System.out.println("----------------一本小說(shuō)完結(jié)后的書(shū)單------------------");
mediator.showNovelList();
//撤銷(xiāo)分類(lèi)2t
Type type = new Type("2t", "奇異");
type.setUnpopular(true);
System.out.println("----------------刪除一個(gè)分類(lèi)后------------------");
mediator.showNovelList();
}
}
這里的Novel和Type都抽象父類(lèi),但依舊都可以當(dāng)作同事類(lèi),NovelTypeMediator則為單例中介者。
六、優(yōu)點(diǎn)和缺點(diǎn)
6.1、優(yōu)點(diǎn)
中介者模式的主要優(yōu)點(diǎn)如下:
- 中介者模式簡(jiǎn)化了對(duì)象之間的交互,它用中介者和同事的一對(duì)多交互代替了原來(lái)同事之間的多對(duì)多交互,一對(duì)多關(guān)系更容易理解、維護(hù)和擴(kuò)展,將原本難以理解的網(wǎng)狀結(jié)構(gòu)轉(zhuǎn)換成相對(duì)簡(jiǎn)單的星型結(jié)構(gòu)。
- 中介者模式可將各同事對(duì)象解耦。中介者有利于各同事之間的松耦合,我們可以獨(dú)立的改變和復(fù)用每一個(gè)同事和中介者,增加新的中介者和新的同事類(lèi)都比較方便,更好地符合“開(kāi)閉原則”。
- 復(fù)用性更好,中介者將原本分布于多個(gè)對(duì)象間的行為集中在一起,改變這些行為只需生成新的中介者子類(lèi)即可,這使各個(gè)同事類(lèi)可被重用,無(wú)須對(duì)同事類(lèi)進(jìn)行擴(kuò)展。
- 將同事類(lèi)的調(diào)用轉(zhuǎn)移到中介者類(lèi)中,簡(jiǎn)化了各同事類(lèi)的設(shè)計(jì)和實(shí)現(xiàn)。
6.2、缺點(diǎn)
中介者模式的主要缺點(diǎn)如下:
- 中介者模式將交互的復(fù)雜性變?yōu)橹薪檎叩膹?fù)雜性,在具體中介者類(lèi)中包含了同事之間的交互細(xì)節(jié),這可能使得中介者自身成為一個(gè)難于維護(hù)的龐然大物。
七、適用環(huán)境
在以下情況下可以使用中介者模式:
- 系統(tǒng)中對(duì)象之間存在復(fù)雜的引用關(guān)系,產(chǎn)生的相互依賴(lài)關(guān)系結(jié)構(gòu)混亂且難以理解。
- 一個(gè)對(duì)象由于引用了其他很多對(duì)象并且直接和這些對(duì)象通信,導(dǎo)致難以復(fù)用該對(duì)象。
- 想通過(guò)一個(gè)中間類(lèi)來(lái)封裝多個(gè)類(lèi)中的行為,而又不想生成太多的子類(lèi)??梢酝ㄟ^(guò)引入中介者類(lèi)來(lái)實(shí)現(xiàn),在中介者中定義對(duì)象。
- 交互的公共行為,如果需要改變行為則可以增加新的中介者類(lèi)。
八、模式應(yīng)用
MVC架構(gòu)中控制器Controller作為一種中介者,它負(fù)責(zé)控制視圖對(duì)象View和模型對(duì)象Model之間的交互。如在Struts中,Action就可以作為JSP頁(yè)面與業(yè)務(wù)對(duì)象之間的中介者。