本文是设计模式学习笔记的第三篇文章,主要分析的是中介者模式。是清华大学出版社的《研读设计模式》的读书笔记。在此感谢原作者。
这次应用的是一个实例去理解中介者模式。
先来看这样一个实例,电脑播放电影。
我们需要一个DVD,然后放入播放器中,播放器获得数据传递给CPU处理在将数据传给显卡和声卡,循环这个流程。简单么?
实际上,如果没有主板,各部件要自行交互的话是非常复杂的,而且各个配件的接口不同,还要进行转化才能匹配,这是多么烦躁的事情。
所以,我们的电脑上会有主板,每个配件只需要做自己的本职工作就好,做好的东西给主板,需要的东西从主板拿。主板就是一个中介者。
在软件开发上,其实就是解耦。如果每个类都需要和其他类进行交互,那么当一个类发生改变的时候,其他类也要改变,我们可不希望这样。
让我们看看应用中介者模式模拟的电脑播放电影的过程。体验下中介者所干的事情。
先准备好CD,在工程下建立一个a.txt文件,内容如下:
READY?GO!,碾碎他们
~\(≧▽≦)/~,啦啦啦德玛西亚
~\(≧▽≦)/~~\(≧▽≦)/~,啦啦啦啦德玛西亚
~\(≧▽≦)/~~\(≧▽≦)/~~\(≧▽≦)/~,德玛德玛德玛西亚
╭︿︿︿╮ ,{/ o o /}
( (oo) ) , ︶ ︶︶
>>>>FIRST BLOOD<<<<,FIRST BLOOD
首先是CD类:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class CD {
//设定cd
private String filePath = null;
private File myFile = null;
public File getFile(){
return myFile;
}
public CD(String filePath){
this.filePath = filePath;
myFile = new File(filePath);
}
}
然后是中介者接口:
public interface Mediator {
/**
* 同时对象在自身改变的时候来通知中介者的方法
* 让中介者去负责相应的与其他同时对象的交互
* @param colleague 同时对象自身,好让中介者对象通过对象实例
* 去获取同时对象的状态
*/
public void changed(Colleague colleague);
}
然后是实现中介者接口的主板类:
public class MotherBoard implements Mediator{
private CDDriver cdDriver = null;
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 cdDriver 光驱对象
*/
public void opeCDDriverReadData(CDDriver cdDriver){
//1:先获取光驱读取的数据
String data = cdDriver.getData();
//2:把这些数据传递给CPU进行处理
this.cpu.executeData(data);
}
public void opeCPU(CPU cpu){
//获取CPU处理后的数据
String videoData = cpu.getVideoData();
String soundData = cpu.getSoundData();
//把这些数据传递给显卡和声卡
this.videoCard.showData(videoData);
this.soundCard.soundData(soundData);
}
}
然后是同事(部件)的父类(抽象类):
public abstract class Colleague {
private Mediator mediator;
public Colleague(Mediator mediator){
this.mediator = mediator;
}
public Mediator getMediator() {
return mediator;
}
}
然后是部件CDDriver类:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 光驱类,一个同事类
*
* @author qian.xu
*
*/
public class CDDriver extends Colleague {
public CDDriver(Mediator mediator) {
super(mediator);
// TODO Auto-generated constructor stub
}
/**
* 光驱读出来的数据
*/
private String data = "";
/**
* 光驱读取出来的数据
*
* @return 光驱读取出来的数据
*/
public String getData() {
return this.data;
}
/**
* 读取光驱
*/
public void readCD(CD cd) {
try {
File file = cd.getFile();
FileReader reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
while ((data = br.readLine()) != null) {
this.getMediator().changed(this);
Thread.sleep(3000);
}
br.close();
reader.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
然后是部件CPU类:
/**
* COU,一个同事类
* @author qian.xu
*
*/
public class CPU extends Colleague{
public CPU(Mediator mediator) {
super(mediator);
// TODO Auto-generated constructor stub
}
/**
* 分解出来的视频数据
*/
private String videoData = "";
/**
* 分解出来的音频数据
*/
private String soundData = "";
/**
* 获取分解出来的视频数据
* @return 分解出来的视频数据
*/
public String getVideoData(){
return videoData;
}
/**
* 获取分解出来的音频数据
* @return 分解出来的音频数据
*/
public String getSoundData(){
return soundData;
}
public void executeData(String data){
//处理数据,分解视频流和音频流
String[] ss = data.split(",");
this.videoData = ss[0];
this.soundData = ss[1];
//通知主板,CPU工作完成
this.getMediator().changed(this);
}
}
然后是部件显卡类:
/**
* 显卡类,一个同事类
* @author qian.xu
*
*/
public class VideoCard extends Colleague{
public VideoCard(Mediator mediator) {
super(mediator);
// TODO Auto-generated constructor stub
}
/**
* 显示视频数据
* @param data 被显示的数据
*/
public void showData(String data){
System.out.println("视频流:"+data);
}
}
最后是部件声卡类:
public class SoundCard extends Colleague{
public SoundCard(Mediator mediator) {
super(mediator);
// TODO Auto-generated constructor stub
}
/**
* 声卡工作
* @param data 要发出的声音
*/
public void soundData(String data){
System.out.println("音频流:"+data);
}
}
终于完事了,让我们打开播放器开电影吧,下面是测试类:
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//创造主板
MotherBoard mediator = new MotherBoard();
//创建各部件
CD cd = new CD("a.txt");
CDDriver cdDriver = new CDDriver(mediator);
CPU cpu = new CPU(mediator);
VideoCard videoCard = new VideoCard(mediator);
SoundCard soundCard = new SoundCard(mediator);
//组装各部件到主板上
mediator.setCdDriver(cdDriver);
mediator.setCpu(cpu);
mediator.setVideoCard(videoCard);
mediator.setSoundCard(soundCard);
//将CD放入播放器中
cdDriver.readCD(cd);
}
输出结果(为了模仿的更像,加入了线程睡眠,有了延迟输出的效果,所有建议将程序跑起来看下):
视频流:READY?GO!
音频流:碾碎他们
视频流:~\(≧▽≦)/~
音频流:啦啦啦德玛西亚
视频流:~\(≧▽≦)/~~\(≧▽≦)/~
音频流:啦啦啦啦德玛西亚
视频流:~\(≧▽≦)/~~\(≧▽≦)/~~\(≧▽≦)/~
音频流:德玛德玛德玛西亚
视频流: ╭︿︿︿╮
音频流: {/ o o /}
视频流: ( (oo) )
音频流: ︶ ︶︶
视频流:>>>>FIRST BLOOD<<<<
音频流:FIRST BLOOD
中介者的本质
中介者的本质是封装交互。中介者的目的,就是用来封装多个对象的交互,这些交互的处理多在中介者对象里面实现。因此中介对象的复杂程度,取决于他封装的交互的复杂程度。
何时选用中介者模式
建议在以下情况时选用中介者模式。
★ 如果一组对象之间的通信方式比较复杂,导致相互依赖、结构混乱。可以采用中介者模式,把这些对象的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。
★ 如果一个对象引用很多的对象,并直接跟这些对些对象交互,导致难以复用该对象,可以采用中介者模式,把这个对象跟其他对象的交互封装到中介者对象里边,这个对象只需要和中介者对象交互就好了。
中介者模式的不足
中介者模式的一个潜在缺点是,过度集中化。如果同事对象的交互非常多,而且比较复杂,当这些复杂性全部集中到中介者的时候,会导致中介者对象变得十分复杂,难于管理和维护。
广义中介者
在应用开发中,经常会简化中介者模式,来使开发变得简单,比如:
★通常会去掉同事对象的父类,这样可以让任意的对象,只需要相互交互,就可以成为同事。
★通常不定义Mediator接口,把具体的中介者对象实现为单例。
★同事对象不再持有中介者,而是在需要的时候直接获取中介者对象并调用,对应的,中介者也不再持有对象同事,而是在具体的处理方法里去创建,或者读取。