基础知识(08) -- 中介者模式

思维导图

----------------------------------------------------------------------

1、场景问题                                                           

 大家都知道电脑的主要配件有:CPU、内存、硬盘、显卡、声卡、网卡、光驱、主板等,这些配件它们之间都是通过主板来完成相互之间的交互工作,但是如果没有了主板会怎么样呢?

  如果没有了主板情况,那么各个配件之间就需要自行相互交互,以相互传送数据,如下图:

  如果有主板的情况,各个配件的交互完全通过主板来完成,每个配件都只需要和主板交互,而主板知道如何和所有的配件交互,如下图:

  如果上面的情况发生在软件开发中呢? 就相当于出现了多个类之间相互交互,而且交互的很频繁,导致每个类都必须知道所有需要交互的类,也就是我们常说的类和类耦合了,是不是很麻烦?那该如何来简化这种多个对象之间的交互呢? ----> 使用中介者模式来解决。

演示案例:程序模拟用电脑来看电影,简化后的流程如下:

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

  第二步:主板去得到光驱的数据,把这些数据交给CPU进行分析处理

  第三步:CPU处理完后,把数据分成了视频数据和音频数据,通知主板,它处理完了

  第四步:主板去得到CPU处理过后的数据,分别把数据交给显卡和声卡,去显示出视频和发出声音

上面的流程是持续的、不断重复的直到电影播放完毕,下面使用程序把这个过程实现出来:

2、解决方案 --- 使用中介者模式来解决问题                              

2.1 中介者模式的定义

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

2.2 使用中介者模式来解决问题的思路

  通过分析上面的问题,根本原因就在于多个对象需要相互交互,从而导致对象之间紧密耦合,不利于对象的修改和维护。

  使用中介者模式解决问题的思路很简单,中介者模式通过引入一个中介者对象,让其他的对象都只和中介对象交互,而中介对象知道如何和其他所有的对象交互,这样对象之间的依赖关系就没有了,从而实现了对象之间的解耦。(对于中介对象而言,所有相互交互的对象,被视为同事类;中介对象就是来维护各个同事之间的关系,而所有的同事类都只是和中介对象交互。)

2.3 中介者模式的结构和说明:

  

说明:

  Mediator: 中介者接口。在里面定义各个同事之间交互需要的方法。

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

  Colleague: 同事类的定义,通常实现成为抽象方法,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能。

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

中介者模式的示例代码

  1 /**
  2  * 同事类的抽象父类  5  */
  6 public abstract class Colleague {
  7 
  8     //持有中介者对象,每一个同事类都知道它的中介者对象
  9     private Mediator mediator;
 10     
 11     /**
 12      * 构造方法,传入中介者对象
 13      * @param mediator
 14      */
 15     public Colleague(Mediator mediator) {
 16         this.mediator = mediator;
 17     }
 18 
 19     /**
 20      * 获取当前同事类对应的中介者对象
 21      * @return
 22      */
 23     public Mediator getMediator() {
 24         return mediator;
 25     }
 26 }
 27 
 28 /**
 29  * 具体的同事类A
 30  */
 31 public class ConcreteColleagueA extends Colleague {
 32 
 33     public ConcreteColleagueA(Mediator mediator) {
 34         super(mediator);
 35     }
 36 
 37     /**
 38      * 示意方法,执行某些业务功能
 39      */
 40     public void someOperation(){
 41         //在需要跟其他同事通信的时候,通知中介者对象
 42         getMediator().change(this);
 43     }
 44 }
 45 
 46 /**
 47  * 具体的同事类B
 48  */
 49 public class ConcreteColleagueB extends Colleague {
 50 
 51     public ConcreteColleagueB(Mediator mediator) {
 52         super(mediator);
 53     }
 54 
 55     /**
 56      * 示意方法,执行某些业务功能
 57      */
 58     public void someOperation(){
 59         //在需要跟其他同事通信的时候,通知中介者对象
 60         getMediator().change(this);
 61     }
 62 }
 63 
 64 
 65 /**
 66  * 中介者,定义各个同事对象通信的接口
 67  */
 68 public interface Mediator {
 69     /**
 70      * 同事对象在自身改变的时候来通知中介者的方法
 71      * 让中介者去负责相应的与其他同事对象的交互
 72      * @param colleague 同事对象自身
 73      */
 74     public void change(Colleague colleague);
 75 }
 76 
 77 /**
 78  * 具体的中介者实现 81  */
 82 public class ConcreteMediator implements Mediator {
 83 
 84     //持有并维护同事A
 85     private ConcreteColleagueA colleagueA;
 86     
 87     //持有并维护同事B
 88     private ConcreteColleagueB colleagueB;
 89     
 90     public ConcreteMediator() {
 91     }
 92 
 93     @Override
 94     public void change(Colleague colleague) {
 95         //某个同事类发生了变化,通常需要与其他同事交互
 96         //具体协调相应的同事对象来实现协作行为
 97     }
 98     
 99     /**
100      * 设置中介者需要了解并维护的同事A对象
101      * @param colleagueA 同事A对象
102      */
103     public void setColleagueA(ConcreteColleagueA colleagueA) {
104         this.colleagueA = colleagueA;
105     }
106 
107     /**
108      * 设置中介者需要了解并维护的同事B对象
109      * @param colleagueB 同事B对象
110      */
111     public void setColleagueB(ConcreteColleagueB colleagueB) {
112         this.colleagueB = colleagueB;
113     }
114 }

 使用中介模式来描述电脑看电影的流程:

  1 /**
  2  * 所有同事的抽象父类
  3  */
  4 public abstract class Colleague {
  5     private Mediator mediator;
  6     
  7     public Colleague(Mediator mediator){
  8         this.mediator = mediator;
  9     }
 10 
 11     public Mediator getMediator() {
 12         return mediator;
 13     }
 14 }
 15 
 16 /**
 17  * 同事类: 光驱类
 18  */
 19 public class CDDriver extends Colleague {
 20     //光驱读取出来的数据
 21     private String data = "";
 22     
 23     public CDDriver(Mediator mediator) {
 24         super(mediator);
 25     }
 26     
 27     /**
 28      * 读取光盘
 29      */
 30     public void readCD(){
 31         this.data = "学习研磨设计模式,中介者模式";
 32         //通知主板,自己的状态发生了改变
 33         this.getMediator().changed(this);
 34     }
 35 
 36     public String getData() {
 37         return data;
 38     }
 39 }
 40 
 41 /**
 42  * 同事类: CPU
 43  */
 44 public class CPU extends Colleague{
 45     //分析出来的视频数据
 46     private String videoData = "";
 47     //分析出来的音频数据
 48     private String soundData = "";
 49     
 50     public CPU(Mediator mediator) {
 51         super(mediator);
 52     }
 53     
 54     /**
 55      * 处理数据,把数据 分成音频和视频的数据
 56      * @param data
 57      */
 58     public void executeData(String data){
 59         //把数据分解开,前面的是视频数据,后面的是音频数据
 60         String[] ss = data.split(",");
 61         this.videoData = ss[0];
 62         this.soundData = ss[1];
 63         
 64         //通知主板,CPU的工作完成了
 65         this.getMediator().changed(this);
 66     }
 67 
 68     public String getVideoData() {
 69         return videoData;
 70     }
 71 
 72     public String getSoundData() {
 73         return soundData;
 74     }
 75 }
 76 
 77 /**
 78  * 同事类 : 显卡类
 79  */
 80 public class VideoCard extends Colleague {
 81 
 82     public VideoCard(Mediator mediator) {
 83         super(mediator);
 84     }
 85 
 86     /**
 87      * 显示视频数据
 88      * @param data 被显示的数据
 89      */
 90     public void showData(String data){
 91         System.out.println("您正观看的是 : " + data);
 92     }
 93 }
 94 
 95 /**
 96  * 同事类: 声卡类
 97  */
 98 public class SoundCard extends Colleague {
 99 
100     public SoundCard(Mediator mediator) {
101         super(mediator);
102     }
103 
104     /**
105      * 按照声频数据发出声音
106      */
107     public void soundData(String data){
108         System.out.println("画外音: " + data);
109     }
110 }
111 
112 /**
113  * 中介者对象的接口
114  */
115 public interface Mediator {
116     /**
117      * 同事对象在自身改变的时候来通知中介者的方法,
118      * 让中介者去负责相应的与其他同事对象的交互
119      * @param colleague 同事对象自身,好让中介者对象通过对象实例去获取同事对象的状态
120      */
121     public void changed(Colleague colleague);
122 }
123 
124 /**
125  * 主板类,实现中介者接口
126  */
127 public class MediatorBoard implements Mediator {
128 
129     //需要知道要交互的同事类--光驱类
130     private CDDriver cdDriver = null;
131     //需要知道要交互的同事类--CPU类
132     private CPU cpu = null;
133     //需要知道要交互的同事类--显卡类
134     private VideoCard videoCard = null;
135     //需要知道要交互的同事类--声卡类
136     private SoundCard soundCard = null;
137     
138     @Override
139     public void changed(Colleague colleague) {
140         if(colleague == cdDriver){
141             //表示光驱读取数据了
142             this.openCDDriverReadData((CDDriver)colleague);
143         }else if(colleague == cpu){
144             //表示CPU处理完了
145             this.openCPU((CPU)colleague);
146         }
147     }
148 
149     private void openCDDriverReadData(CDDriver cdDriver){
150         //1. 先获取光驱读取的数据
151         String data = cdDriver.getData();
152         //2. 把这些数据传递给CPU进行处理
153         this.cpu.executeData(data);
154     }
155     
156     /**
157      * 处理CPU处理完数据后与其他对象的交互
158      */
159     private void openCPU(CPU cpu){
160         //1. 先获取CPU处理后的数据
161         String videoData = cpu.getVideoData();
162         String soundData = cpu.getSoundData();
163         //2. 把这些数据传递给显卡和声卡
164         this.videoCard.showData(videoData);
165         this.soundCard.soundData(soundData);
166     }
167     
168     public void setCdDriver(CDDriver cdDriver) {
169         this.cdDriver = cdDriver;
170     }
171 
172     public void setCpu(CPU cpu) {
173         this.cpu = cpu;
174     }
175 
176     public void setVideoCard(VideoCard videoCard) {
177         this.videoCard = videoCard;
178     }
179 
180     public void setSoundCard(SoundCard soundCard) {
181         this.soundCard = soundCard;
182     }
183 }
184 
185 /*
186 测试类
187 */
188 public class Client {
189     public static void main(String[] args) {
190         //1.创建中介者---主板对象
191         MediatorBoard mediator = new MediatorBoard();
192         
193         //创建同事类
194         CDDriver cd = new CDDriver(mediator);
195         CPU cpu = new CPU(mediator);
196         VideoCard vc = new VideoCard(mediator);
197         SoundCard sc = new SoundCard(mediator);
198         
199         //让中介者知道所有的同事
200         mediator.setCdDriver(cd);
201         mediator.setCpu(cpu);
202         mediator.setVideoCard(vc);
203         mediator.setSoundCard(sc);
204         
205         //开始看电影
206         cd.readCD();
207     }
208 }

3、模式讲解                                                           

3.1中介者模式的功能

  中介者模式的功能非常简单,就是封装对象之间的交互。把所有对象之间的交互封装在中介者当中,无形中还可以得到另外一个好处,就是能够集中地控制这些对象的交互关系,这样当有变化的时候,修改起来就很方便。

3.2 需要Mediator接口吗?

  首先要明白接口是用来实现"封装隔离的",那么封装谁?隔离谁呢? Mediator接口就是用来封装中介者对象的,使得使用中介者对象的同事对象跟具体的中介者实现分离开。那么有没有使用Mediator接口的必要,那就取决于是否会提供多个不同的中介者实现,如果中介者实现只有一个的话,而且预计中也没有需要扩展的要求,那么就可以不定义Mediator接口,让各个同事类直接使用中介者实现对象。

3.3 中介者模式的调用顺序示意图

4、广义中介者                                                         

查看上面标准的中介者模式的结构、定义和示例后,会发现几个问题,使得中介者模式在实际使用的时候,变得繁琐和困难:

问题一:是否有必要要同事对象定义一个公共的父类?

  Java是单继承的,如果为了使用中介者模式,就让这些同事对象继承了一个父类,这是很不好的。

问题二:同事类有必要持有中介者对象吗?

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

问题三:是否需要中介者接口?

  在实际开发中,很常见的情况是不需要中介者接口的,中介者通常实现成单列。

问题四:中介者对象是否需要持有所有的同事?

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

  在示例的公共方法里,需要去区分到底是谁调的。在实际开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是什么对象,具体是什么业务了.

基于上面的考虑,在实际应用开发中,经常会简化中介者模式,来使开发变得简单,比如有如下的简化

  1.通常会去掉同事对象的父类,这样就可以让任意的对象,只要需要相互交互,就可以成为同事。

  2.通常不定义Mediator接口,把具体的中介者对象实现成为单列

  3.同事对象不再持久中介者,而是在需要的时候直接获取中介者对象并调用

  4.中介者也不再持有同事对象,而是在具体处理方法里面去创建,或者获取,或者从参数传入需要的同事对象

经过上面4个步骤的简化、变形使用的情况称为广义中介者

5、思考中介者模式                                                     

5.1 中介者模式的优缺点

  优点:

    1.松散耦合。 中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互不依赖。

    2.集中控制交互。 多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了。

    3.多对多变成一对多。没有使用中介者模式的时候,同事对象之间的关系通常是多对多的,引入中介者对象以后,中介者对象和同事对象的关系通常就变成了双向的一对多。

  缺点:  

    中介者模式的一个潜在缺点:过度集中化。如果同事对象的交互非常多,而且比较复杂,当这些复杂性全部集中到中介者的时候,会导致中介者对象变得十分复杂,而且难于管理和维护。

5.2 中介者模式的本质

  中介者模式的本质:封装交互。 

5.3 何时选用中介者模式

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

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

 

 ----------------------------------------------------

 

参考:

 

  《研磨设计模式》

 

 

posted @ 2017-01-10 20:28  火爆泡菜  阅读(311)  评论(0编辑  收藏  举报