本文參考自:《JAVA設(shè)計(jì)模式》之中介者模式(Mediator)
1. 作用
中介者模式也稱為調(diào)停者模式,調(diào)停者模式是對(duì)象的行為模式。調(diào)停者模式包裝了一系列對(duì)象相互作用的方式,使得這些對(duì)象不必相互明顯引用。從而使它們可以較松散地耦合。當(dāng)這些對(duì)象中的某些對(duì)象之間的相互作用發(fā)生改變時(shí),不會(huì)立即影響到其他的一些對(duì)象之間的相互作用。從而保證這些相互作用可以彼此獨(dú)立地變化。
2. 需要中介者模式的原因
如下圖所示,這個(gè)示意圖中有大量的對(duì)象,這些對(duì)象既會(huì)影響別的對(duì)象,又會(huì)被別的對(duì)象所影響,因此常常叫做同事(Colleague)對(duì)象。這些同事對(duì)象通過(guò)彼此的相互作用形成系統(tǒng)的行為。從圖中可以看出,幾乎每一個(gè)對(duì)象都需要與其他的對(duì)象發(fā)生相互作用,而這種相互作用表現(xiàn)為一個(gè)對(duì)象與另一個(gè)對(duì)象的直接耦合。這就是過(guò)度耦合的系統(tǒng)。
通過(guò)引入調(diào)停者對(duì)象(Mediator),可以將系統(tǒng)的網(wǎng)狀結(jié)構(gòu)變成以中介者為中心的星形結(jié)構(gòu),如下圖所示。在這個(gè)星形結(jié)構(gòu)中,同事對(duì)象不再通過(guò)直接的聯(lián)系與另一個(gè)對(duì)象發(fā)生相互作用;相反的,它通過(guò)調(diào)停者對(duì)象與另一個(gè)對(duì)象發(fā)生相互作用。調(diào)停者對(duì)象的存在保證了對(duì)象結(jié)構(gòu)上的穩(wěn)定,也就是說(shuō),系統(tǒng)的結(jié)構(gòu)不會(huì)因?yàn)樾聦?duì)象的引入造成大量的修改工作。
3. 結(jié)構(gòu)
中介者模式的四個(gè)角色
-
抽象的中介者(Mediator)
定義出同事對(duì)象到調(diào)停者對(duì)象的接口,其中主要方法是一個(gè)(或多個(gè))事件方法。 -
具體的中介者(ConcreteMediator)
實(shí)現(xiàn)了抽象調(diào)停者所聲明的事件方法。具體調(diào)停者知曉所有的具體同事類,并負(fù)責(zé)具體的協(xié)調(diào)各同事對(duì)象的交互關(guān)系。 -
抽象的同事類(Colleague)
定義出調(diào)停者到同事對(duì)象的接口。同事對(duì)象只知道調(diào)停者而不知道其余的同事對(duì)象。 -
具體的同事類(ConcreteColleague)
所有的具體同事類均從抽象同事類繼承而來(lái)。實(shí)現(xiàn)自己的業(yè)務(wù),在需要與其他同事通信的時(shí)候,就與持有的調(diào)停者通信,調(diào)停者會(huì)負(fù)責(zé)與其他的同事交互。
4. 實(shí)現(xiàn)
抽象的中介者
public interface Mediator {
void change(Colleague c);
}
具體的中介者
public class ConcreteMediator implements Mediator{
private ConcreteColleagueA colleagueA;
private ConcreteColleagueB colleagueB;
public void setColleagueA(ConcreteColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
public void setColleagueB(ConcreteColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
@Override
public void change(Colleague c) {
if(c instanceof ConcreteColleagueA) {
System.out.println(((ConcreteColleagueA) c).getName());
}else if(c instanceof ConcreteColleagueB) {
System.out.println(((ConcreteColleagueB) c).getName());
}
}
}
抽象的同事類
public abstract class Colleague {
private Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public Mediator getMediator() {
return this.mediator;
}
public abstract void operate();
}
具體的同事類
public class ConcreteColleagueA extends Colleague{
private String name;
public ConcreteColleagueA(Mediator mediator) {
super(mediator);
}
public String getName() {
return this.name;
}
@Override
public void operate() {
this.name = "colleagueA";
getMediator().change(this);
}
}
public class ConcreteColleagueB extends Colleague{
private String name;
public ConcreteColleagueB(Mediator mediator) {
super(mediator);
}
public String getName() {
return this.name;
}
@Override
public void operate() {
this.name = "colleagueB";
getMediator().change(this);
}
}
客戶端調(diào)用
public class MediatorPatternMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
mediator.setColleagueA(colleagueA);
mediator.setColleagueB(colleagueB);
colleagueA.operate();
colleagueB.operate();
}
}
5. 具體實(shí)例
在日常生活中,我們經(jīng)常使用電腦來(lái)看電影,把這個(gè)過(guò)程描述出來(lái),簡(jiǎn)化后假定會(huì)有如下的交互過(guò)程:
(1)首先是光驅(qū)要讀取光盤上的數(shù)據(jù),然后告訴主板,它的狀態(tài)改變了。
(2)主板去得到光驅(qū)的數(shù)據(jù),把這些數(shù)據(jù)交給CPU進(jìn)行分析處理。
(3)CPU處理完后,把數(shù)據(jù)分成了視頻數(shù)據(jù)和音頻數(shù)據(jù),通知主板,它處理完了。
(4)主板去得到CPU處理過(guò)后的數(shù)據(jù),分別把數(shù)據(jù)交給顯卡和聲卡,去顯示出視頻和發(fā)出聲音。
要使用調(diào)停者模式來(lái)實(shí)現(xiàn)示例,那就要區(qū)分出同事對(duì)象和調(diào)停者對(duì)象。很明顯,主板是調(diào)停者,而光驅(qū)、聲卡、CPU、顯卡等配件,都是作為同事對(duì)象。
抽象的中介者
public interface Mediator {
void change(Colleague colleague);
}
具體的中介者-主板類
public class MainBoard implements Mediator{
private CDReader cdReader;
private CPU cpu;
private SoundPlayer soundPlayer;
private VideoPlayer videoPlayer;
public void setCDReader(CDReader cdReader) {
this.cdReader = cdReader;
}
public void setCPU(CPU cpu) {
this.cpu = cpu;
}
public void setSoundPlayer(SoundPlayer soundPlayer) {
this.soundPlayer = soundPlayer;
}
public void setVideoPlayer(VideoPlayer videoPlayer) {
this.videoPlayer = videoPlayer;
}
@Override
public void change(Colleague colleague) {
if(colleague instanceof CDReader) {
String cdReaderData = ((CDReader)colleague).getData();
operateCPU(cdReaderData);
}else if(colleague instanceof CPU) {
List<String> cpuDataList = ((CPU)colleague).getDataList();
operateSoundPlayer(cpuDataList.get(0));
operateVideoPlayer(cpuDataList.get(1));
}
}
private void operateCPU(String data) {
cpu.operate(data);
}
private void operateSoundPlayer(String data) {
soundPlayer.playSound(data);
}
private void operateVideoPlayer(String data) {
videoPlayer.playVideo(data);
}
}
抽象的同事類
public class Colleague {
private Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public Mediator getMediator() {
return this.mediator;
}
}
具體的同事類-光驅(qū)類
public class CDReader extends Colleague{
private String data;
public CDReader(Mediator mediator) {
super(mediator);
}
public String getData() {
return this.data;
}
public void read() {
this.data = "sound,video";
getMediator().change(this);
}
}
具體的同事類-CPU類
public class CPU extends Colleague{
private List<String> dataList;
public CPU(Mediator mediator) {
super(mediator);
dataList = new ArrayList<>();
}
public List<String> getDataList(){
return dataList;
}
public void operate(String data) {
String[] liStrings = data.split(",");
dataList.add(liStrings[0]);
dataList.add(liStrings[1]);
getMediator().change(this);
}
}
具體的同事類-聲卡類
public class SoundPlayer extends Colleague{
public SoundPlayer(Mediator mediator) {
super(mediator);
}
public void playSound(String sound) {
System.out.println(sound);
}
}
具體的同事類-顯卡類
public class VideoPlayer extends Colleague{
public VideoPlayer(Mediator mediator) {
super(mediator);
}
public void playVideo(String video) {
System.out.println(video);
}
}
客戶端調(diào)用
public class MediatorPatternDemoMain {
public static void main(String[] args) {
MainBoard mainBoard = new MainBoard();
CDReader cdReader = new CDReader(mainBoard);
CPU cpu = new CPU(mainBoard);
SoundPlayer soundPlayer = new SoundPlayer(mainBoard);
VideoPlayer videoPlayer = new VideoPlayer(mainBoard);
mainBoard.setCDReader(cdReader);
mainBoard.setCPU(cpu);
mainBoard.setSoundPlayer(soundPlayer);
mainBoard.setVideoPlayer(videoPlayer);
cdReader.read();
}
}
6. 中介者模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
-
松散耦合
調(diào)停者模式通過(guò)把多個(gè)同事對(duì)象之間的交互封裝到調(diào)停者對(duì)象里面,從而使得同事對(duì)象之間松散耦合,基本上可以做到互補(bǔ)依賴。這樣一來(lái),同事對(duì)象就可以獨(dú)立地變化和復(fù)用,而不再像以前那樣“牽一處而動(dòng)全身”了。
-
集中控制交互
多個(gè)同事對(duì)象的交互,被封裝在調(diào)停者對(duì)象里面集中管理,使得這些交互行為發(fā)生變化的時(shí)候,只需要修改調(diào)停者對(duì)象就可以了,當(dāng)然如果是已經(jīng)做好的系統(tǒng),那么就擴(kuò)展調(diào)停者對(duì)象,而各個(gè)同事類不需要做修改。 -
多對(duì)多變成一對(duì)多
沒(méi)有使用調(diào)停者模式的時(shí)候,同事對(duì)象之間的關(guān)系通常是多對(duì)多的,引入調(diào)停者對(duì)象以后,調(diào)停者對(duì)象和同事對(duì)象的關(guān)系通常變成雙向的一對(duì)多,這會(huì)讓對(duì)象的關(guān)系更容易理解和實(shí)現(xiàn)。
缺點(diǎn)
調(diào)停者模式的一個(gè)潛在缺點(diǎn)是,過(guò)度集中化。如果同事對(duì)象的交互非常多,而且比較復(fù)雜,當(dāng)這些復(fù)雜性全部集中到調(diào)停者的時(shí)候,會(huì)導(dǎo)致調(diào)停者對(duì)象變得十分復(fù)雜,而且難于管理和維護(hù)。