`
喻红叶
  • 浏览: 41087 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

Java与模式-中介者模式

 
阅读更多

一个系统或者模块的类和类之间的交互如下图所示:


从图中可以看出,每一个类几乎都与另外所有的类有交互,如果类A发生了修改,那么类B,D,E,G,I等类也需要随着修改;反过来,如果其他几个类有变动,那么类A也需要修改。这个系统中,类与类之间耦合度太高,如果是一个真正的系统,那么会有比这多的多的类,如果类与类这间也是这样的交互关系,那就真是牵一发而动全身了,这个系统几乎就是无法维护,无法修改,无法扩展的。这是一个过度耦合的系统,在软件开发中,我们一定要避免这种情况的发生。可是该如何避免呢?

上图中的繁杂在于多个对象之间都有交互,从而导致耦合过密,不利于对象的修改和扩展。如果我们再引进一个对象扮演中间人的角色,所有的对象都与中间人通信,所有的交互都由中间人来处理。一旦某个对象作出了修改,它只需要通知中间人,由中间人来协调其他对象,反过来,其他对象的修改也不会影响到这个对象。系统的类图由上面的网状结构变成下面的星状结构:


对象之间的关系就很简单,它们只与中间人耦合,而不需要知道其他对象的状态。把上面的关系再提炼一下,就得到一种设计模式。

中介者(Mediator)模式

中介者模式的定义:用一个中介对象来封装一些列的对象交互,中介者使得各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式解决问题的思路很简单,就是通过引入一个中介对象,让其他对象只与中介对象交互,而中介对象知道如何和其他所有对象的交互,这样对象之间的交互关系就没有了,从而实现了对象之间的解耦。由此,我们也可以看出一个问题,那就是中介对象控制着整个系统的逻辑,它会过于复杂,这是一个缺点。中介者模式的本质是封装交互

(1)对象在自身状态发生改变时报告给中介对象;

(2)中介对象控制着整个系统的逻辑,它知道如何与所有对象交互;

(3)对象需要对中介对象发出的请求作出回应。

中介者模式中的角色:

Mediator:中介者接口,定义各个同事之间交互所需要的方法;

ConcreteMediator:具体的中介者,它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系;

Colleague:所有同事对象的父类,一般实现成抽象类,主要负责约束同事对象的类型,并负责实现一些公共功能;

ConcreteMediator:具体的同事类,实现自己的业务,当需要与其他同事对象通信时,就与持有的中介者通信,中介者会负责与其他同事的交互。在标准的中介者模式中,将使用中介者来交互的那些对象叫做同事类,它们继承自相同的父类,所以叫做同事。正是由于它们之间的交互很复杂,所以才产生了把这些交互关系分离出去,让中介者来处理。

还是用些代码来清晰的展示一下中介者模式。以电脑来看电影为例子,首先光驱从光盘中读取数据,然后通知CPU将数据分离成音频和视频,CPU处理完毕后再分别将数据传送给声卡和显卡进行播放。从上面的描述的中发现,光驱盒CPU是耦合的,CPU又和声卡显卡是耦合的,怎么解耦的呢?如果使用中介者模式,通过引入主板作为中介者,所有的对象都与主板交互,那么播放电影的流程就变成了这样:

(1)光驱从光盘读取到数据,通知主板,数据准备好了;

(2)主板收到光驱的请求后,将原始数据传给CPU,让它将数据分离成音频和视频;

(3)CPU将数据分离后,通知主板,数据分离完毕;

(4)主板收到CPU通知后,分别将音频和视频传给声卡和显卡;

(5)声卡和显卡同时播放。

这样一个过程中,所有的类只与主板耦合,而不与其他类保持关系,做到了解耦,而且过程很清晰。实际上计算机硬件就是这样通信的,只不过更复杂一些,所以这些东西都是相通的,重要的是思想。下面给出示意性的代码,首先是同事类:

/**
 * 同事对象的父类,一般实现成抽象类,用于约束同事对象的类型
 * 同时实现一些功能公共方法,例如持有中介者对象
 */
public abstract class Colleague {
	//所有的同事对象都需要持有中介对象
	private Mediator mediator;
	public Colleague(Mediator mediator) {
		this.mediator = mediator;
	}
	
	public Mediator getMediator() {
		return mediator;
	}
}

/*光驱类,负责从光盘中读取数据*/
class CDDriver extends Colleague {
	//从光盘读取的原始数据
	private String originData;
	public CDDriver(Mediator mediator) {
		super(mediator);
	}
	
	public String getOriginData() {
		return originData;
	}
	
	/*读取光盘数据,一旦读取到数据,就要通知中介者对象数据已经准备好了*/
	public void readCD() {
		originData = "巴萨罗那,简称巴萨";
		//通知中介对象,自己的状态发生了改变
		getMediator().changed(this);
	}
}

/*CPU类,负责将原始数据分离成音频和视频*/
class CPU extends Colleague {
	//声音数据
	private String soundData;
	//视频数据
	private String videoData;
	
	public CPU(Mediator mediator) {
		super(mediator);
	}
	
	public String getSoundData() {
		return soundData;
	}
	public String getVideoData() {
		return videoData;
	}

	/*将数据分离,同时通知中介者对象,数据已经分离*/
	public void sperateData(String originData) {
		this.soundData = originData.split(",")[1];
		this.videoData = originData.split(",")[0];
		
		//通知中介对象,自己的状态发生了改变
		getMediator().changed(this);
	}
}

/*显卡类,播放视频*/
class VideoCard extends Colleague {
	public VideoCard(Mediator mediator) {
		super(mediator);
	}
	
	public void showVideo(String videoData) {
		System.out.println("正在观看:" + videoData);
	}
}
/*声卡类,播放声音*/
class SoundCard extends Colleague {
	public SoundCard(Mediator mediator) {
		super(mediator);
	}
	
	public void showSound(String soundData) {
		System.out.println("解说:" + soundData);
	}
}
中介者:
package com.javaandpatterns.mediator;
/*中介者接口*/
public interface  Mediator {
	//同事对象自身状态改变时,通过这个方法通知中介者对象
	void changed(Colleague obj);
	
	//中介者对象需要知道所有同事对象
	public void setCDDriver(CDDriver instance);
	public void setCPU(CPU instance);
	public void setVideoCard(VideoCard instance);
	public void setSoundCard(SoundCard instance);
}

class MainBoard implements Mediator {
	private CDDriver cd;
	private CPU cpu;
	private VideoCard vc;
	private SoundCard sc;

	public void setCDDriver(CDDriver instance) {
		this.cd = instance;
	}
	public void setCPU(CPU instance) {
		this.cpu = instance;
	}
	public void setVideoCard(VideoCard instance) {
		this.vc = instance;
	}
	public void setSoundCard(SoundCard instance) {
		this.sc = instance;
	}
	
	/**
	 * 当同时对象自身状态发生改变时,调用此方法通知中介者对象
	 * 中介者对象在进行逻辑控制,与其他同对象交互
	 */
	public void changed(Colleague obj) {
		//如果是光驱类,需要通知CPU去分离数据
		if(obj instanceof CDDriver) {
			String originData = ((CDDriver) obj).getOriginData();
			this.cpu.sperateData(originData);
		}else if(obj instanceof CPU){//如果是CPU类,需要通知声卡和显卡去播放
			String videoData = ((CPU) obj).getVideoData();
			String soundData = ((CPU) obj).getSoundData();
			this.vc.showVideo(videoData);
			this.sc.showSound(soundData);
		}
	}	
}
运行的一个例子:
package com.javaandpatterns.mediator;

public class Client {

	public static void main(String[] args) {
		Mediator 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.setSoundCard(sc);
		mediator.setVideoCard(vc);
		
		//光驱读数据,通知中介者,中介者通知CPU去分离数据,CPU分离数据完成,通知中介者,中介者通知声卡和显卡播放
		cd.readCD();
	}
}
讲解

中介者的功能就是封装对象之间的交互,它集中控制系统的交互。

需不需要Mediator接口?接口是用来封装隔离的,Mediator接口用来封装中介者对象,让同事对象与中介者对象隔离开来,让同事对象面向中介者接口编程。了解这点之后,那就需要看系统的实际需求。如果系统中需要不止一个中介者实现,或者预计中会有扩展的需求,那么就需要Mediator接口;如果系统只需要一个中介者实现,预计未来也不会有扩展,那么就不需要Mediator接口。

同事对象如何与中介者对象通信?可以像上面实例中那样,定义一个通用的changed方法,并把同事对象作为参数传入,这样在中介者对象里就可以获取同事对象的实例,进而做其他操作。更具体的,中介者对象可以为具体的同事对象实现具体的方法,这样便于进行简洁的逻辑控制。在上面的实例中,中介者对象可以为CDDriver类专门实现一个CDChanged方法,这样交互的话就更清晰,实现起来也不容易出错:

public void CDChanged(String originData) {
		this.cpu.sperateData(originData);
}
模式的优点:

(1)松散耦合,消除了同事对象之间的耦合;

(2)集中控制交互,同事对象之间的交互都被封装到中介者对象中,如果交互发生变化,只需要修改中介者对象就可以了;

(3)将多对多的关系变成一对多,让对象关系更加清晰,容易实现,其实还是解耦。

缺点:多度集中化,如果同事对象之间的交互比较多且比较复杂,中介者在封装这些交互时也会变得更加复杂,而且难于管理和维护。

使用场景

中介者模式是被滥用的最多的设计模式之一,在使用中介者模式之前,一定要正确分析系统的需求,同时正确理解中介者模式,然后再决定是否使用此模式。

(1)如果一组对象之间交互复杂,导致相互依赖,结构混乱,可以采用中介者模式,使各个对象松散耦合,结构清晰;

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

广义中介者模式

仔细研究上面的中介者模式会发现存在几个问题:

1.必须为同事类定义公共的父类。在实际的开发中,很多相互交互的对象本身没有公共的父类,强行加上一个父类会破坏这个类的结构,引起诸多不合理的实现,这是很没必要的。如果强行加上公共父类,继承它也得不到任何好处。如果有内在逻辑关系,当然可以定义公共的父类。

2.上面定义了中介者接口。在实际开发中,很常见的是不需要中介者接口,中介者对象也不需要有很多实例。中介者对象是用来封装和处理同事对象关系的,它一般是没有状态需要维护的,因此中介者对象可以实现成单例。

3.同事对象需要持有中介者对象吗?同事对象肯定需要知道中介者对象,但是否有必要把中介者对象作为属性并通过构造方法传入这么强的依赖关系?其实可以通过简单的方式通知中介者对象,比如中介者对象实现为单例,同事对象可以直接调用。

4.中介者对象是否有必要持有同事对象?和上面一样,最好是通过更简单的方式进行传递,比如方法参数等。

基于上面的考虑,在实际应用开发中,经常会简化中介者模式:

(1)通常会去掉同事对象的父类,这样可以让任意的交互的对象成为同事;

(2)通常不定义Mediator接口,把具体的中介者对象实现为单例;

(3)同事对象不再持有中介者对象,在需要的时候直接调用中介者对象;中介者对象也不再持有同事对象,在具体的处理方法里去创建,或者获取,或者从参数传入需要的同事对象;

(4)中介者对象根据实际的逻辑提供具体的业务通知方法,就像在上面“讲解”的最后一段讨论的那样。

把这样经过变形和简化的情况成为广义中介者模式。我觉得广义中介者模式更靠谱,更有实用价值。

管理系统中都有部分管理和人员管理,抽象出来部门类Dep和人员类User,经常需要处理的情况是人员离职,部门合并,人员转部门,撤销部门,不论是哪一种情况,都需要处理相应的人员和部门,人员对象和部门对象被紧密的耦合在一起。这时候就可以使用中介者模式了,可是部门类和人员类在逻辑上能有公共的父类吗?好像是不能有,所以这就需要使用广义中介者模式。中介者对象可以实现成单例,对于上面的每一种情况可以实现单独的业务方法,通过方法参数传入同事对象,不再持有同事对象。下面是示意代码:

/*部门类*/
public class Dep {
	//部门编号
	private String depId;
	//部门名称
	private String depName;
	//setter和getter
	//.......
	
	/*撤销部门,通知中介者模式*/
	public boolean deleteDep() {
		//获取中介者对象
		DepUserMediatorImp mediator = DepUserMediatorImp.getInstance();
		//通知中介者对象处理部门撤销事物
		mediator.deleteDep(depId);
		return true;
	}
}

/*人员类*/
class User {
	//人员编号
	private String userId;
	//人员名称
	private String userName;
	//setter和getter
	//.......
	
	/*撤销人员,通知中介者模式*/
	public boolean deleteUser() {
		//获取中介者对象
		DepUserMediatorImp mediator = DepUserMediatorImp.getInstance();
		//通知中介者对象处理人员撤销事物
		mediator.deleteUser(userId);
		return true;
	}
}
中介者对象
/*实现成单例类*/
public class DepUserMediatorImp {
	private static DepUserMediatorImp instance = new DepUserMediatorImp();
	
	/*部分和人员关系*/
	static class DepUserModel {
		private String depUserId;
		private String depId;
		private String userId;
		
		//setter,getter方法
	}
	//保存部门和人员关系
	private Collection<DepUserModel> model = ....;
	
	public static DepUserMediatorImp getInstance() {
		return instance;
	}
	
	/*部门撤销,通知中介者对象*/
	public boolean deleteDep(String depId) {
		//处理该部门人员的业务
		
		//删除当前model中与该部门有关系的对象
		Collection<DepUserModel> tmp = new ArrayList<DepUserModel>();
		for(DepUserModel m : model)
			if(m.getDepId().equals(depId))
				tmp.add(m);
		//删除
		model.removeAll(tmp);
	}
	
	/*人员离职,通知中介者对象*/
	public boolean deleteUser(String userId) {
		//删除当前model中与该部门有关系的对象
		Collection<DepUserModel> tmp = new ArrayList<DepUserModel>();
		for(DepUserModel m : model)
			if(m.getUserId().equals(userId))
				tmp.add(m);
		//删除
		model.removeAll(tmp);
	}
}

转载请注明出处:喻红叶《Java与模式-中介者模式》

分享到:
评论

相关推荐

    JAVA-设计模式-行为型模式-中介者模式

    JAVA-设计模式-行为型模式-中介者模式

    java设计模式-中介者模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段...

    java与模式-阎宏

    总的来说,《Java与模式》是一本非常实用的书籍,它将理论与实践相结合,既适合初学者系统学习设计模式,也适合有经验的开发者作为参考。通过阅读和实践书中的内容,读者可以提升自己的编程技能,编写出更高质量、更...

    Java设计模式-建造者模式详解

    Java设计模式-建造者模式详解将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。生成器模式(Builder)是使用多个“小型”工厂来最终创建出一个完整对象。当我们使用Builder的时候,...

    java设计模式---诙谐易懂版

    根据给定文件内容,以下是关于Java设计模式的知识点说明: 1. 策略模式(Strategy Pattern)是一种行为设计模式,允许在运行时选择算法的行为。策略模式的意图是定义一系列算法,将每个算法封装起来,并使它们可以...

    java设计模式----建造者模式

    java设计模式------------------------------------建造者模式

    Java与模式---闫宏

    《Java与模式---闫宏》这本书是针对Java程序员深入理解设计模式的重要参考资料。设计模式是软件工程中的一个重要概念,它们代表了在特定上下文中解决常见问题的最佳实践。这本书结合了中国的道家思想,以一种独特的...

    java设计模式-访问者模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段...

    JAVA-设计模式-行为型模式-访问者模式

    JAVA-设计模式-行为型模式-访问者模式

    JAVA-设计模式-创建型模式-建造者模式

    JAVA-设计模式-创建型模式-建造者模式

    JAVA-设计模式-行为型模式-观察者模式

    JAVA-设计模式-行为型模式-观察者模式

    java常用设计模式-中介者模式

    中介者模式(Mediator Pattern)是一种行为设计模式,它允许对象之间通过中介者对象进行通信,从而避免对象之间的直接耦合。中介者模式将对象之间的复杂关系转化为中介者和对象之间的简单关系,从而提高了系统的灵活...

    Java设计模式----通俗易懂版

    这个资源"Java设计模式----通俗易懂版"显然是一个专门针对初学者或需要深入理解设计模式的开发者编写的指南。作者以形象生动的例子解释了23种经典的Java设计模式,使得复杂的概念变得更加易于理解。 首先,我们要...

    2.java设计模式-创建者模式-工厂方法模式.pdf

    java设计模式-创建者模式-简单工厂模式。详细的讲解了什么是工厂方法模式及应用场景和应用场景的代码事例。及各工厂模式的区别。

    JAVA设计模式-chm版

    这个“JAVA设计模式-chm版”资源显然包含了关于Java设计模式的详细信息,便于理解和应用。设计模式是对常见问题的解决方案的标准化描述,它们在软件工程中起到了重要的作用,帮助开发者创建可维护、可扩展且易于理解...

    Java与模式-清晰书签版

    本书《Java与模式-清晰书签版》不仅是初学者了解Java和设计模式的优秀教材,也是经验丰富的开发者巩固和提升技能的宝贵资源。 总的来说,这本《Java与模式-清晰书签版》将帮助读者深入理解Java语言,掌握设计模式的...

    设计模式--装饰者模式java例子

    装饰者模式是软件设计模式中的一种结构型模式,它...综上所述,装饰者模式在Java编程中是一种重要的设计模式,尤其适用于需要动态添加或删除对象功能的场景。通过以上示例和解释,我们可以更好地理解和应用装饰者模式。

    java-设计模式-状态模式-模拟审批流程-二级审批 完整代码

    总之,这个Java实现展示了如何利用设计模式来处理复杂的审批流程。状态模式在这里发挥着关键作用,它将审批流程的各个阶段抽象为独立的类,使得系统可以根据审批状态的变化灵活地改变其行为。这种设计思路对于构建...

    java与模式-清晰书签版

    首先,文档《Java设计模式-图解-附代码.doc》提供了对设计模式的图形化解释,并结合了实际的Java代码示例。设计模式是软件开发中的最佳实践,它们是为了解决常见问题而发展起来的可复用解决方案。通过图解,读者可以...

    Java设计模式-代理模式例子

    在这个“Java设计模式-代理模式例子”中,我们将深入探讨代理模式的概念、实现方式以及它在实际开发中的应用。 代理模式的核心思想是为一个对象提供一个替身,这个替身即代理对象,代理对象控制对原对象的访问。在...

Global site tag (gtag.js) - Google Analytics