目录
本文的结构如下:
- 引言
- 什么是中介者模式
- 模式的结构
- 典型代码
- 代码示例
- 优点和缺点
- 适用环境
- 模式应用
一、引言
日常开发是离不开电脑的,这需要cpu、内存、显卡、键盘、显示器等零件相互调用,如果直接让这些零件互相调用,它们之间的关系会很复杂:
显然这样造成的后果是难以维护,为了避免这种情况,开发商引入了主板,由主板和各部件进行交互,统一协调,这样每个部件只需要把命令传给主板,由主板决定同哪个部件交互,然后接受主板返回的数据即可,不需要知道其他部件的存在:
在软件开发中,面向对象设计鼓励将行为分布到各个对象中。这种分布可能会导致对象间有许多连接。在最坏的情况下,每一个对象都知道其他所有对象。
将一个系统分割成许多对象通常可以增强可复用性,但是对象间相互连接的激增又会降低其可复用性。大量的相互连接使得一个对象似乎不太可能在没有其他对象的支持下工作—--系统表现为一个不可分割的整体(过度耦合)。而且对系统的行为进行任何较大的改动都十分困难。
这时可以像电脑开发商一样引入“主板”来解决,这就是中介者模式。
二、什么是中介者模式
如果在一个系统中对象之间存在多对多的相互关系(像没有引入主板前电脑各部件,它们被称为“同事类”),这时可以将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行统一协调,这样对象之间多对多的复杂关系就转化为相对简单的一对多关系。通过引入中介者来简化对象之间的复杂交互,中介者模式是“迪米特法则”的一个典型应用。
中介者模式定义如下:
中介者模式(Mediator Pattern):用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。
中介者模式解决的困境就是多个对象之间的相互引用导致的紧耦合,通过引入一个中介者,原来互相引用的对象就相互解耦了,他们之间现在没有任何关系,只和中介者交互。
说白了中介者模式把对象之间的多对多关系转换成了同中介者类的一对多关系,从而降低耦合。
三、模式的结构
中介者模式UML类图如下:
中介者模式将一个网状的系统结构变成一个以中介者对象为中心的星形结构,在这个星型结构中,使用中介者对象与其他对象的一对多关系来取代原有对象之间的多对多关系。在中介者模式结构图中包含如下几个角色:
- Mediator(抽象中介者):中介者接口,该接口用于与各同事对象之间进行通信。
- ConcreteMediator(具体中介者):具体中介者实现对象,它维持对各个同事对象的引用,协调各个同事对象来实现协作行为。
- Colleague(抽象同事类):主要负责约束同事对象的类型,它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。
- ConcreteColleague(具体同事类):在需要与其他同事通信的时候,就与持有的中介者通信,中介者负责与其他的同事进行交互;在具体同事类中实现了在抽象同事类中声明的抽象方法。
中介者模式的核心是中介者类,它承担了两方面的职责:
- 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。
- 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
四、典型代码
抽象中介者类典型代码如下:
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!");
}
}
}
抽象同事类典型代码:
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);
}
具体同事类典型代码:
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);
}
}
客户端测试:
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、标准中介者模式
假设使用电脑播放视频,把步骤分为如下几步:
- 光驱读取光盘内容,把读取到的内容传递给主板。
- 主板得到内容,交给cpu处理。
- cpu处理完毕,把处理后的数据传递给主板。
- 主板把数据传递给显卡,显卡显示视频(忽略了显示器显示这个步骤)。
不使用中介者模式
public class CDDriver{
private String data;
private CPU cpu;
public CDDriver(){
this.cpu = new CPU();
}
public String getData() {
return data;
}
public void readCD(){
//逗号前是视频数据,逗号后是音频数据
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){
//分解数据,前面是视频数据,后面是音频数据
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("你听到的声音是:" + data);
}
}
这里设计的关联其实还算简单,并没有很复杂,但仍然会发现有以下问题:
- 系统耦合度高:每组件与多个其他组件之间产生相互关联和调用,若一个组件对象发生变化,需要跟踪与之有关联的其他所有组件并进行处理,组件之间的耦合度高。
- 组件的可重用性差:由于每一个组件和其他组件之间都具有很强的关联,若没有其他组件的支持,一个组件很难被另一个系统或模块重用,这些组件表现出来更像一个不可分割的整体,而在实际使用时,我们往往需要每一个组件都能够单独重用,而不是重用一个由多个组件组成的复杂结构。
- 系统的可扩展性差:如果在上述系统中增加一个新的组件类,则必须修改与之交互的其他组件类的源代码,将导致多个类的源代码需要修改,同样,如果要删除一个组件也存在类似的问题,这违反了“开闭原则”,可扩展性和灵活性欠佳。
使用中介者模式
抽象中介者:
public interface Mediator {
void changed(Colleague colleague);
}
具体中介者主板类:
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){
//表示光驱读取了数据
this.operateCDDriverReadData((CDDriver)colleague);
}else if(colleague == cpu){
//表示CPU处理完数据
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);
}
}
抽象同事类:
public abstract class Colleague {
private final Mediator mediator;
public Colleague(Mediator mediator){
this.mediator = mediator;
}
public Mediator getMediator(){
return mediator;
}
}
具体同事类:
public class CDDriver extends Colleague {
private String data;
public CDDriver(Mediator mediator) {
super(mediator);
}
public String getData() {
return data;
}
public void readCD(){
//逗号前是视频数据,逗号后是音频数据
this.data = "Video Data,Sound Data";
//通知主板,自己的状态发生了变化
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){
//分解数据,前面是视频数据,后面是音频数据
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("你听到的声音是:" + data);
}
}
客户端测试:
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();
}
}
引入中介者类后,再来看就会发现组件之间不再相互关联,系统之间耦合度降低,更易复用,扩展和维护,当然会发现系统相关变得复杂。
5.2、更广义的中介者模式
我们知道现实开发中,很多时候不会完全符合标准中介者模式,这时可以实际情况做一些变化,比如:
- 去掉同事对象的父类,这样可以让任意的对象,只要需要相互交互,就可以成为同事。
- 不定义Mediator接口,把具体的中介者实现成单例。
- 同事对象不再持有中介者对象,而是在具体处理方法里面去创建或者获取,或者从参数传入需要的同事对象。
现假设有有小说类和类别类,为此制定了一个书单,书单上有平时追更的小说,当小说完结了,也就追更完毕,就把小说从书单删除;还有可以将冷门类别的小说从书单删除......
这样实现:
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) {
//如果完结了,就从书单删除
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();
//书单
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);
}
/**
* 展示书单
*/
public void showNovelList(){
for (NovelTypeRelation relation : novelList){
System.out.println("小说编号是:" + relation.getNovelId() + ", 类型编号是:" + relation.getTypeId());
}
}
/**
* 删除小说
* @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();
}
}
}
/**
* 删除类型
* @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();
}
}
}
}
测试类:
public class Client {
public static void main(String[] args) {
NovelTypeMediator mediator = NovelTypeMediator.getInstance();
//展示书单
mediator.showNovelList();
//完结
Novel novel = new Novel("1n", "诛仙");
novel.setFinsh(true);
System.out.println("----------------一本小说完结后的书单------------------");
mediator.showNovelList();
//撤销分类2t
Type type = new Type("2t", "奇异");
type.setUnpopular(true);
System.out.println("----------------删除一个分类后------------------");
mediator.showNovelList();
}
}
这里的Novel和Type都抽象父类,但依旧都可以当作同事类,NovelTypeMediator则为单例中介者。
六、优点和缺点
6.1、优点
中介者模式的主要优点如下:
- 中介者模式简化了对象之间的交互,它用中介者和同事的一对多交互代替了原来同事之间的多对多交互,一对多关系更容易理解、维护和扩展,将原本难以理解的网状结构转换成相对简单的星型结构。
- 中介者模式可将各同事对象解耦。中介者有利于各同事之间的松耦合,我们可以独立的改变和复用每一个同事和中介者,增加新的中介者和新的同事类都比较方便,更好地符合“开闭原则”。
- 复用性更好,中介者将原本分布于多个对象间的行为集中在一起,改变这些行为只需生成新的中介者子类即可,这使各个同事类可被重用,无须对同事类进行扩展。
- 将同事类的调用转移到中介者类中,简化了各同事类的设计和实现。
6.2、缺点
中介者模式的主要缺点如下:
- 中介者模式将交互的复杂性变为中介者的复杂性,在具体中介者类中包含了同事之间的交互细节,这可能使得中介者自身成为一个难于维护的庞然大物。
七、适用环境
在以下情况下可以使用中介者模式:
- 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
- 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。
- 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。可以通过引入中介者类来实现,在中介者中定义对象。
- 交互的公共行为,如果需要改变行为则可以增加新的中介者类。
八、模式应用
MVC架构中控制器Controller作为一种中介者,它负责控制视图对象View和模型对象Model之间的交互。如在Struts中,Action就可以作为JSP页面与业务对象之间的中介者。