初识中介者模式
定义
用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式 地相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。
结构和说明
- Mediator: 中介者接口。在里面定义各个同事之间交互需要的方法,可以是公共的通 讯方法,比如changed方法,大家都用,也可以是小范围的交互方法。 - ConcreteMediator: 具体中介者实现对象。它需要了解并维护各个同事对象,并负责具体的协 调各同事对象的交互关系。 - Colleague: 同事类的定义,通常实现成为抽象类,主要负责约束同事对象的类型,并 实现一些具体同事类之间的公共功能,比如:每个具体同事类都应该知道中介者 对象,也就是具体同事类都会持有中介者对象,就可以定义到这个类里面。 - ConcreteColleague: 具体的同事类,实现自己的业务,在需要与其它同事通讯的时候,就与持 有的中介者通信,中介者会负责与其它的同事交互。
示例代码
同事类的抽象父类
package cn.javass.dp.mediator.example1;
/**
* 同事类的抽象父类
*/
public abstract class Colleague {
/**
* 持有中介者对象,每一个同事类都知道它的中介者对象
*/
private Mediator mediator;
/**
* 构造方法,传入中介者对象
* @param mediator 中介者对象
*/
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
/**
* 获取当前同事类对应的中介者对象
* @return 对应的中介者对象
*/
public Mediator
getMediator() {
return mediator;
}
}
具体的同事类A
package cn.javass.dp.mediator.example1;
/**
* 具体的同事类A
*/
public class ConcreteColleagueA extends Colleague {
public ConcreteColleagueA(Mediator mediator) {
super(mediator);
}
/**
* 示意方法,执行某些业务功能
*/
public void someOperation() {
getMediator().changed(
this);
}
}
具体的同事类B
package cn.javass.dp.mediator.example1;
/**
* 具体的同事类B
*/
public class ConcreteColleagueB extends Colleague {
public ConcreteColleagueB(Mediator mediator) {
super(mediator);
}
/**
* 示意方法,执行某些业务功能
*/
public void someOperation() {
getMediator().changed(
this);
}
}
中介者,定义各个同事对象通信的接口
package cn.javass.dp.mediator.example1;
/**
* 中介者,定义各个同事对象通信的接口
*/
public interface Mediator {
/**
* 同事对象在自身改变的时候来通知中介者的方法,
* 让中介者去负责相应的与其他同事对象的交互
* @param colleague 同事对象自身,好让中介者对象通过对象实例
* 去获取同事对象的状态
*/
public void changed(Colleague colleague);
}
具体的中介者实现
package cn.javass.dp.mediator.example1;
/**
* 具体的中介者实现
*/
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处理过后的数据,分别把数据交给显卡和声卡,去显示出视频和 发出声音
当然这是一个持续的、不断重复的过程,从而形成不间断的视频和声音, 具体的运行过程不在讨论之列,假设就有如上简单的交互关系就可以了。也就是 说想看电影,把光盘放入光驱,光驱开始读盘,就可以看电影了
使用模式的解决方案
示例代码
同事类的抽象父类
package cn.javass.dp.mediator.example2;
/**
* 同事类的抽象父类
*/
public abstract class Colleague {
/**
* 持有中介者对象,每一个同事类都知道它的中介者对象
*/
private Mediator mediator;
/**
* 构造方法,传入中介者对象
* @param mediator 中介者对象
*/
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
/**
* 获取当前同事类对应的中介者对象
* @return 对应的中介者对象
*/
public Mediator
getMediator() {
return mediator;
}
}
光驱类,一个同事类
package cn.javass.dp.mediator.example2;
/**
* 光驱类,一个同事类
*/
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);
}
}
CPU类,一个同事类
package cn.javass.dp.mediator.example2;
/**
* 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];
this.getMediator().changed(
this);
}
}
声卡类,一个同事类
package cn.javass.dp.mediator.example2;
/**
* 声卡类,一个同事类
*/
public class SoundCard extends Colleague{
public SoundCard(Mediator mediator) {
super(mediator);
}
/**
* 按照声频数据发出声音
* @param data 发出声音的数据
*/
public void soundData(String data){
System.out.println(
"画外音:"+data);
}
}
显卡类,一个同事类
package cn.javass.dp.mediator.example2;
/**
* 显卡类,一个同事类
*/
public class VideoCard extends Colleague{
public VideoCard(Mediator mediator) {
super(mediator);
}
/**
* 显示视频数据
* @param data 被显示的数据
*/
public void showData(String data){
System.out.println(
"您正观看的是:"+data);
}
}
中介者对象的接口
package cn.javass.dp.mediator.example2;
/**
* 中介者对象的接口
*/
public interface Mediator {
/**
* 同事对象在自身改变的时候来通知中介者的方法,
* 让中介者去负责相应的与其他同事对象的交互
* @param colleague 同事对象自身,好让中介者对象通过对象实例
* 去获取同事对象的状态
*/
public void changed(Colleague colleague);
}
主板类,实现中介者接口
package cn.javass.dp.mediator.example2;
/**
* 主板类,实现中介者接口
*/
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){
this.opeCPU((CPU)colleague);
}
}
/**
* 处理光驱读取数据过后与其他对象的交互
* @param cd 光驱同事对象
*/
private void opeCDDriverReadData(CDDriver cd){
String data = cd.getData();
this.cpu.executeData(data);
}
/**
* 处理CPU处理完数据后与其他对象的交互
* @param cpu CPU同事类
*/
private void opeCPU(CPU cpu){
String videoData = cpu.getVideoData();
String soundData = cpu.getSoundData();
this.videoCard.showData(videoData);
this.soundCard.soundData(soundData);
}
}
客户端
package cn.javass.dp.mediator.example2;
public class Client {
public static void main(String[] args) {
MotherBoard mediator =
new MotherBoard();
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();
}
}
理解中介者模式
认识中介者模式
1:中介者模式的功能
中介者的功能非常简单,就是封装对象之间的交互。
如果一个对象的操作 会引起其它相关对象的变化,或者是某个操作需要引起其它对象的后续或连带操 作,而这个对象又不希望自己来处理这些关系,那么就可以找中介者,把所有的 麻烦扔给它,只在需要的时候通知中介者,其它的就让中介者去处理就可以了。
反过来,其它的对象在操作的时候,可能会引起这个对象的变化,也可以 这么做。
最后对象之间就完全分离了,谁都不直接跟其它对象交互,那么相互的 关系,全部被集中到中介者对象里面了,所有的对象就只是跟中介者对象进行通 信,相互之间不再有联系。
把所有对象之间的交互都封装在中介者当中,无形中还得到另外一个好 处,就是能够集中的控制这些对象的交互关系,这样有什么变化的时候,修改起 来就很方便。
有没有使用Mediator接口的必要,取决于是否会提供多个不同的中介者实 现。如果中介者实现只有一个的话,而且预计中也没有需要扩展的要求,那么就 可以不定义Mediator接口,让各个同事对象直接使用中介者实现对象;如果中介 者实现不只一个,或者预计中有扩展的要求,那么就需要定义Mediator接口,让 各个同事对象来面向中介者接口编程,而无需关心具体的中介者实现。
3:同事关系
在中介者模式中,要求这些类都要继承相同的类,也就是说,这些对象从 某个角度讲是同一个类型,算是兄弟对象。
正是这些兄弟对象之间的交互关系很复杂,才产生了把这些交互关系分离 出去,单独做成中介者对象,这样一来,这些兄弟对象就成了中介者对象眼里的 同事。
4:同事和中介者的关系
中介者对象和同事对象之间是相互依赖的 。
5:如何实现同事和中介者的通信
一种实现方式是在Mediator接口中定义一个特殊的通知接口,作为一个通 用的方法,让各个同事类来调用这个方法 。
另外一种实现方式是可以采用观察者模式,把Mediator实现成为观察者, 而各个同事类实现成为Subject,这样同事类发生了改变,会通知Mediator。
Mediator在接到通知过后,会与相应的同事对象进行交互。
6:中介者模式的调用顺序示意图
广义中介者
标准的中介者模式在实际使用中的困难
1:是否有必要为同事对象定义一个公共的父类?
大家都知道,Java是单继承的,为了使用中介者模式,就让这些同事对象 继承一个父类,这是很不好的;再说了,这个父类目前也没有什么特别的公共功 能,也就是说继承它也得不到多少好处。 在实际开发中,很多相互交互的对象本身是没有公共父类的,强行加上一 个父类,会让这些对象实现起来特别别扭。
2:同事类有必要持有中介者对象吗?
同事类需要知道中介者对象,以便当它们发生改变的时候,能够通知中介 者对象,但是否需要作为属性,并通过构造方法传入,这么强的依赖关系呢?
也可以有简单的方式去通知中介对象,比如把中介对象做成单例,直接在 同事类的方法里面去调用中介者对象。
3:是否需要中介者接口?
在实际开发中,很常见的情况是不需要中介者接口的,而且中介者对象也不需要创 建很多个实例,因为中介者是用来封装和处理同事对象的关系的,它一般是没有状态需要 维护的,因此中介者通常可以实现成单例。
4:中介者对象是否需要持有所有的同事?
虽说中介者对象需要知道所有的同事类,这样中介者才能与它们交互。但是是否需 要做为属性这么强烈的依赖关系,而且中介者对象在不同的关系维护上,可能会需要不同 的同事对象的实例,因此可以在中介者处理的方法里面去创建、或者获取、或者从参数传 入需要的同事对象。
5:中介者对象只是提供一个公共的方法,来接受同事对象的通知吗?
从示例就可以看出来,在公共方法里,还是要去区分到底是谁调过来,这还是简单 的,还没有去区分到底是什么样的业务触发调用过来的,因为不同的业务,引起的与其它 对象的交互是不一样的。
因此在实际开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是 什么对象,具体是什么业务了。
对标准的中介者模式在实际使用中的改进
基于上面的考虑,在实际应用开发中,经常会简化中介者模式,来使开发 变得简单,比如有如下的简化: - 1:通常会去掉同事对象的父类,这样可以让任意的对象,只要需要相互交互,就可 以成为同事; - 2:还有通常不定义Mediator接口,把具体的中介者对象实现成为单例; - 3:另外一点就是同事对象不再持有中介者,而是在需要的时候直接获取中介者对象 并调用;中介者也不再持有同事对象,而是在具体处理方法里面去创建、或者获 取、或者从参数传入需要的同事对象。
把这样经过简化、变形使用的情况称为广义中介者。
广义中介者示例——部门与人员
1:部门和人员的关系 : 是 多对多的
2:问题的出现
想想部门和人员的功能交互,举几个常见的功能: - (1)部门被撤销 - (2)部门之间进行合并 - (3)人员离职 - (4)人员从一个部门调职到另外一个部门
想想要实现这些功能,按照前面的设计,该怎么做呢? - (1)系统运行期间,部门被撤销了,就意味着这个部门不存在了,可是原来这个部 门下所有的人员,每个人员的所属部门中都有这个部门呢,那么就需要先通知所 有的人员,把这个部门从它们的所属部门中去掉,然后才可以清除这个部门。
(2)部门合并,是合并成一个新的部门呢,还是把一个部门并入到另一个部门?
如 果是合并成一个新的部门,那么需要把原有的两个部门撤销,然后再新增一个部 门;如果是把一个部门合并到另一个部门里面,那就是撤销掉一个部门,然后把 这个部门下的人员移动到这个部门。不管是那种情况,都面临着需要通知相应的 人员进行更改这样的问题。 - (3)人员离职了,反过来就需要通知他所属于的部门,从部门的拥有人员的记录中 去除掉这个人员。 - (4)人员调职,同样需要通知相关的部门,先从原来的部门中去除掉,然后再到新 的部门中添加上。
看了上述的描述,感觉如何?
麻烦的根源在什么地方呢?仔细想想,对了,麻烦的根源就在于部门和人 员之间的耦合,这样导致操作人员的时候,需要操作所有相关的部门,而操作部 门的时候又需要操作所有相关的人员,使得部门和人员搅和在了一起。
3:中介者来解决
找到了根源就好办了,采用中介者模式,引入一个中介者对象来管理部门 和人员之间的关系,就能解决这些问题了。
如果采用标准的中介者模式,想想上面提出的那些问题点吧,就知道实现 起来会很别扭。因此采用广义的中介者来解决,这样部门和人员就完全解耦了, 也就是说部门不知道人员,人员也不知道部门,它们完全分开,它们之间的关系 就完全由中介者对象来管理了。
示例代码
人员类
package cn.javass.dp.mediator.example3;
/**
* 人员类
*/
public class User{
/**
* 人员编号
*/
private String userId;
/**
* 人员名称
*/
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(){
DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
mediator.deleteUser(userId);
return true;
}
}
部门类
package cn.javass.dp.mediator.example3;
import java.util.ArrayList;
import java.util.Collection;
/**
* 部门类
*/
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(){
DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
mediator.deleteDep(depId);
return true;
}
}
实现部门和人员交互的中介者实现类
package cn.javass.dp.mediator.example3;
import java.util.*;
/**
* 实现部门和人员交互的中介者实现类
* 说明:为了演示的简洁性,只示例实现撤销部门和人员离职的功能
*/
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) {
Collection<DepUserModel> tempCol =
new ArrayList<DepUserModel>();
for(DepUserModel du : depUserCol){
if(du.getDepId().equals(depId)){
tempCol.add(du);
}
}
depUserCol.removeAll(tempCol);
return true;
}
/**
* 完成因人员离职引起的与部门的交互
* @param userId 离职的人员的编号
* @return 是否正确处理了因人员离职引起的与部门的交互
*/
public boolean deleteUser(String userId) {
Collection<DepUserModel> tempCol =
new ArrayList<DepUserModel>();
for(DepUserModel du : depUserCol){
if(du.getUserId().equals(userId)){
tempCol.add(du);
}
}
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;
}
}
描述部门和人员关系的类
package cn.javass.dp.mediator.example3;
/**
* 描述部门和人员关系的类
*/
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;
}
}
客户端
package cn.javass.dp.mediator.example3;
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:如果一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象。 可以采用中介者模式,把这个对象跟其它对象的交互封装到中介者对象里面,这 个对象就只需要和中介者对象交互就可以了