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

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

    参考:

      《研磨设计模式》

     

  • 相关阅读:
    下载文件
    全局处理程序
    缩略图
    图片
    文件上传
    application用法
    多分辨率的支持
    适用于cocos2dx的编辑器:Texture,Tilemap,Particle,Action,Level etc
    cocos2dx下最大纹理大小取决于平台
    CCLabelTTF 如何支持换行符和换行
  • 原文地址:https://www.cnblogs.com/xinhuaxuan/p/6266646.html
Copyright © 2011-2022 走看看