大话设计模式二十五:中介者模式(mediator)

一. 定义:

用一个中介对象来封装一系列对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立的改变它们之间的交互。

假设电脑没有主板,那么各个配件之间就必须自行相互交互,以互相传送数据,理论上基本上每个配件相互之间都存在交互数据的可能。

大话设计模式二十五:中介者模式(mediator)_第1张图片

现在有了主板,各个配件的交互完全通过主板来完成,每个配件都只需要和主板交互,而主板知道如何和所有的配件打交道,那就简单多了。

大话设计模式二十五:中介者模式(mediator)_第2张图片

二. 结构和说明:

大话设计模式二十五:中介者模式(mediator)_第3张图片

Mediator:中介者接口,在里面定义各个同事之间交互需要的方法,可以是公共的通讯方法,比如changed方法,大家都用

public interface Mediator {
	/**
	 * 同事对象在自身改变的时候来通知中介者的方法,
	 * 让中介者去负责相应的与其他同事对象的交互
	 * @param colleague 同事对象自身,好让中介者对象通过对象实例
	 *                  去获取同事对象的状态
	 */
	public void changed(Colleague colleague);
}
ConcreteMediator:具体中介者实现对象,它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系。
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) {
		//某个同事类发生了变化,通常需要与其他同事交互
		//具体协调相应的同事对象来实现协作行为
	}

}
Colleague:同事类的定义,通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如:每个具体同事类都应该知道中介者对象,也就是具体同事类都会持有中介者对象,就可以定义到这个类里面。
public abstract class Colleague {
	/**
	 * 持有中介者对象,每一个同事类都知道它的中介者对象
	 */
	private Mediator mediator;
	/**
	 * 构造方法,传入中介者对象
	 * @param mediator 中介者对象
	 */
	public Colleague(Mediator mediator) {
		this.mediator = mediator;
	}
	/**
	 * 获取当前同事类对应的中介者对象
	 * @return 对应的中介者对象
	 */
	public Mediator getMediator() {
		return mediator;
	}
}

ConcreteColleague:具体的同事类,实现自己的业务,在需要与其他同事通讯的时候,就与持有的中介者通信,中介者会负责与其他的同事交互。

public class ConcreteColleagueA extends Colleague {
	public ConcreteColleagueA(Mediator mediator) {
		super(mediator);
	}
	/**
	 * 示意方法,执行某些业务功能
	 */
	public void someOperation() {
		//在需要跟其他同事通信的时候,通知中介者对象
		getMediator().changed(this);
	}
}

三. 现实例子:

现在我们需要用电脑来看电影,假设有如下的交互过程:

1. 首先是光驱要读取光盘上的数据,然后告诉主板它的状态改变了

2. 主板去得到光驱的数据,并把这些数据交给cpu进行分析处理

3. cpu处理完后,把数据分成了视频数据和音频数据,通知主板,它处理完了

4. 主板去得到cpu处理过后的数据,分别把数据交给显卡和声卡,去显示出视频和发出声音

① 中介者对象的接口:

public interface Mediator {
	/**
	 * 同事对象在自身改变的时候来通知中介者的方法,
	 * 让中介者去负责相应的与其他同事对象的交互
	 * @param colleague 同事对象自身,好让中介者对象通过对象实例去获取同事对象的状态
	 */
	public void changed(Colleague colleague);
}
②. 主板类,实现中介者接口
public class MainBoard 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 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{
	// 解出来的视频数据
	private String videoData = "";
	
	// 分解出来的声音数据
	private String soundData = "";
	
	public CPU(Mediator mediator) {
		super(mediator);
	}
	
	/**
	 * 获取分解出来的视频数据
	 * @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{
	// 光驱读取出来的数据
	private String data = "";
	
	public CDDriver(Mediator mediator) {
		super(mediator);
	}
	
	/**
	 * 获取光驱读取出来的数据
	 * @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 class Client {
	public static void main(String[] args) {
		//1:创建中介者——主板对象
		MainBoard mainBoard = new MainBoard();
		
		//2:创建同事类
		CDDriver cd = new CDDriver(mainBoard);
		CPU cpu = new CPU(mainBoard);
		VideoCard vc = new VideoCard(mainBoard);
		SoundCard sc = new SoundCard(mainBoard);
		
		//3:让中介者知道所有的同事
		mainBoard.setCdDriver(cd);
		mainBoard.setCpu(cpu);
		mainBoard.setVideoCard(vc);
		mainBoard.setSoundCard(sc);
		
		//4:开始看电影,把光盘放入光驱,光驱开始读盘
		cd.readCD();
	}
}



四. 认识中介者模式:

中介者模式就是封装对象之间的交互,如果一个对象的操作会引起其他相关对象的变化或连带操作,而这个对象又不希望自己来处理这些关系,那么就可以找中介者。最后对象之间就完全分离了,谁都不直接跟其他对象交互,相互的关系全部集中到中介者对象里面,所有的对象就只跟中介者对象进行通信,相互之间不再有联系。

所有对象之间的交互都封装在中介者,无形中还得到另外一个好处,就是能够集中的控制这些对象的交互关系,这样有什么变化的时候,修改起来很方便。


五. 理解中介者模式:

标准的中介者模式在实际使用中的困难:

1. 是否有必要为同事对象定义一个公共的父类?

java是单继承,为了使用中介者模式,就要强迫这些同事对象继承一个父类,这不好,况且这个父类并没有什么特别的公共方法,也就是说通过继承它并没有得到什么好处。

2. 同事类有必要持有中介者对象吗?

同事类需要知道中介者对象,以便当它们发生改变的时候,能够通知中介者对象,但是是否需要作为属性,并通过构造方法传入这么强的依赖关系吗?

其实有更简单的方式通知中介对象,比如把中介对象做成单例,直接在同事类的方法里面去调用中介者对象。

3. 是否需要中介者接口?

在实际开发中,很常见的情况是不需要中介者接口的,而且中介者对象也不需要创建很多个实例,因为中介者是用来封装和处理同事对象的关系的,它一般是没有状态需要维护的,因此中介者通常可以实现成单例。

4. 中介者对象是否需要持有所有的同事?

虽说中介者对象需要知道所有的同事类,这样中介者才能与他们交互,但是是否需要作为属性这么强烈的依赖关系呢?其实我们可以在中介者处理的方法里面去创建,或者获取,或者从参数传入需要的同事对象。

5. 中介者对象只是提供一个公共的方法来接受同事对象的通知吗?

在实际开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是什么对象,具体是什么业务了。


六. 中介者模式优缺点:

1. 松散耦合

2. 集中控制交互

3. 过度集中化


七. 何时选用中介者模式:

1.如果一组对象之间的通信方式比较复杂,导致相互依赖,结构混乱,可以采用中介者模式,把这些对象相互的交互管理起来,各个对象只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。

2.如果一个对象引用很多的对象,并直接和这些对象交互,导致难以复用该对象。可以采用中介者模式,把这个对象跟其他对象的交互封装到中介者对象里面,这个对象就只需要和中介者对象交互就可以了。 




你可能感兴趣的:(设计模式,中介,中介者)