您的位置:首页 > 其它

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

2017-01-10 20:28 183 查看
思维导图



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

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

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

参考:

  《研磨设计模式》

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: