0%

设计模式之Mediator模式

Mediator模式又称为仲裁者模式或者中介者模式,所起的作用是仲裁和中介,帮助其它类之间进行交流。在仲裁者模式之中,我们要明确两个概念,那就是仲裁者(Mediator)和组员(Colleague),不管组员有什么事情,都会向仲裁者汇报,仲裁者会根据全局的实际情况向其他Colleague作出指示,共同完成一定的逻辑功能。

介绍

  • 意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
    简单说就是封装对象之间的交互.
  • 主要解决了:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
  • 应用实例:MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

实现

  • 示例1:

    ChatRoom.java (仲裁者)
    收到族权的汇报,后处理
    1
    2
    3
    4
    5
    6
    7
    8
    package com.edu.tju.GOF.Mediator.demo2;

    public class ChatRoom {
    public static void showMessage(User user, String message){
    System.out.println(new Date().toString()
    + " [" + user.getName() +"] : " + message);
    }
    }
    User.java(同事类)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.edu.tju.GOF.Mediator.demo2;
    public class User {
    private String name;

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public User(String name){
    this.name = name;
    }

    public void sendMessage(String message){
    ChatRoom.showMessage(this,message);
    }
    }
    MediatorPatternDemo.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.edu.tju.GOF.Mediator.demo2;
    public class MediatorPatternDemo {
    public static void main(String[] args) {
    User robert = new User("Robert");
    User john = new User("John");

    robert.sendMessage("Hi! John!");
    john.sendMessage("Hello! Robert!");
    }
    }

    输出结果:
    Thu Jan 31 16:05:46 IST 2013 [Robert] : Hi! John!
    Thu Jan 31 16:05:46 IST 2013 [John] : Hello! Robert!

robert和john发送消息的请求由仲裁者处理.

  • 示例2
    电脑里面的各个配件之间的交互,主要是通过主板来完成的.如果电脑里面没有了主板,那么各个配件之间就必须自行相互交互,以互相传送数据,理论上说,基本上各个配件相互之间都存在交互数据的可能。如图所示:

    看起来是不是很复杂,其实由于各个配件的接口不同,那么相互之间交互的时候,还必须把数据接口进行转换才能匹配上,那就更恐怖了.
    不过缩影有主板,各个配件的交互完全通过主板来完成,每个配件都只需要和主板交互,而主板知道如何和所有的配件打交道.

下面是一个 使用电脑来看电影的例子,主要分四步

  1. 首先是光驱要读取光盘上的数据,然后告诉主板,它的状态改变了;
  2. 主板去得到光驱的数据,把这些数据交给CPU进行分析处理;
  3. CPU处理完后,把数据分成了视频数据和音频数据,通知主板,它处理完了;
  4. 主板去得到CPU处理过后的数据,分别把数据交给显卡和声卡,去显示出视频和发出声音;

  1. 所有同时的抽象父类的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public abstract class Colleague {
    private Mediator mediator;
    public Colleague(Mediator mediator) {
    this.mediator = mediator;
    }
    public Mediator getMediator() {
    return mediator;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    /**
    * 光驱类,一个同事类
    */
    public class CDDriver extends Colleague{
    public CDDriver(Mediator mediator) {
    super(mediator);
    }
    /**
    * 光驱读取出来的数据
    */
    private String data = "";
    /**
    * 获取光驱读取出来的数据
    * @return 光驱读取出来的数据
    */
    public String getData(){
    return this.data;
    }
    /**
    * 读取光盘
    */
    public void readCD(){
    //逗号前是视频显示的数据,逗号后是声音
    this.data = "设计模式,值得好好研究";
    //通知主板,自己的状态发生了改变
    this.getMediator().changed(this);
    }
    }

    /**
    * CPU类,一个同事类
    */
    public class CPU extends Colleague{
    public CPU(Mediator mediator) {
    super(mediator);
    }
    /**
    * 分解出来的视频数据
    */
    private String videoData = "";
    /**
    * 分解出来的声音数据
    */
    private String soundData = "";
    /**
    * 获取分解出来的视频数据
    * @return 分解出来的视频数据
    */
    public String getVideoData() {
    return videoData;
    }
    /**
    * 获取分解出来的声音数据
    * @return 分解出来的声音数据
    */
    public String getSoundData() {
    return soundData;
    }
    /**
    * 处理数据,把数据分成音频和视频的数据
    * @param data 被处理的数据
    */
    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);
    }
    /**
    * 显示视频数据
    * @param data 被显示的数据
    */
    public void showData(String data){
    System.out.println("您正观看的是:"+data);
    }
    }

    /**
    * 声卡类,一个同事类
    */
    public class SoundCard extends Colleague{
    public SoundCard(Mediator mediator) {
    super(mediator);
    }
    /**
    * 按照声频数据发出声音
    * @param data 发出声音的数据
    */
    public void soundData(String data){
    System.out.println("画外音:"+data);
    }
    }
  2. 中介者接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 中介者对象的接口
    */
    public interface Mediator {
    /**
    * 同事对象在自身改变的时候来通知中介者的方法,
    * 让中介者去负责相应的与其它同事对象的交互
    * @param colleague 同事对象自身,好让中介者对象通过对象实例
    * 去获取同事对象的状态
    */
    public void changed(Colleague colleague);
    }
  3. 实现中介者对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    /**
    * 主板类,实现中介者接口
    */
    public class MotherBoard implements Mediator{
    /**
    * 需要知道要交互的同事类——光驱类
    */
    private CDDriver cdDriver = null;
    /**
    * 需要知道要交互的同事类——CPU类
    */
    private CPU cpu = null;
    /**
    * 需要知道要交互的同事类——显卡类
    */
    private VideoCard videoCard = null;
    /**
    * 需要知道要交互的同事类——声卡类
    */
    private SoundCard soundCard = null;

    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 changed(Colleague colleague) {
    if(colleague == cdDriver){
    //表示光驱读取数据了
    this.opeCDDriverReadData((CDDriver)colleague);
    }else if(colleague == cpu){
    //表示CPU处理完了
    this.opeCPU((CPU)colleague);
    }
    }
    /**
    * 处理光驱读取数据过后与其它对象的交互
    * @param cd 光驱同事对象
    */
    private void opeCDDriverReadData(CDDriver cd){
    //1:先获取光驱读取的数据
    String data = cd.getData();
    //2:把这些数据传递给CPU进行处理
    this.cpu.executeData(data);
    }
    /**
    * 处理CPU处理完数据后与其它对象的交互
    * @param cpu CPU同事类
    */
    private void opeCPU(CPU cpu){
    //1:先获取CPU处理过后的数据
    String videoData = cpu.getVideoData();
    String soundData = cpu.getSoundData();
    //2:把这些数据传递给显卡和声卡展示出来
    this.videoCard.showData(videoData);
    this.soundCard.soundData(soundData);
    }
    }
  4. Client

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Client {
    public static void main(String[] args) {
    //1:创建中介者——主板对象
    MotherBoard mediator = new MotherBoard();
    //2:创建同事类
    CDDriver cd = new CDDriver(mediator);
    CPU cpu = new CPU(mediator);
    VideoCard vc = new VideoCard(mediator);
    SoundCard sc = new SoundCard(mediator);

    //3:让中介者知道所有的同事
    mediator.setCdDriver(cd);
    mediator.setCpu(cpu);
    mediator.setVideoCard(vc);
    mediator.setSoundCard(sc);

    //4:开始看电影,把光盘放入光驱,光驱开始读盘
    cd.readCD();
    }
    }
  • 示例3:(比较复杂不放这了)
    源码

总结

对我们的程序而言,仲裁者模式适合于某一个部分发生改变就会导致其他部分做出相应的改变的情况,我们将这种变换的规律抽象出来,变成仲裁者,从而很好的协商各个部分,当组员有问题的时候(状态发生改变),直接告诉仲裁者,不与其他组员打交道,让仲裁者负责这些繁琐的事务,这样条理就很清晰了,因此仲裁者也成为中介者,可以想象很多人要去租房,很多人要把房子出租,如果私下里面去商量,既浪费时间,又很难找到对方,通过中介这个对象,它收集了很多的租房和出租房的信息,这样就能很快的找到最适合的房子。由此可见生活就是最好的设计模式。仲裁者模式对于代码的修改(很容易定位错误)、新的成员的加入、代码的复用(组员部分可以复用,仲裁者部分不易复用)都有着一定的简化,将该集中处理的集中起来,将该分散的分散出去,无疑是一种好的设计模式。对比于外观模式Facade,仲裁者需要和组员沟通,是双向的,而外观模式facade角色只是对其他角色进行整合,是单向的。

Mediator 模式中的角色

  • Mediator(仲裁者、中介者)
    Mediator 角色负责定义与 Colleague 角色进行通信和做出决定的接口。
  • ConcreteMediator(具体的仲裁者、中介者)
    ConcreteMediator 角色负责实现 Mediator 角色的接口,负责实际做出决定。它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系
  • Colleague(同事)
    同事类的定义,通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如:每个具体同事类都应该知道中介者对象,也就是具体同事类都会持有中介者对象,就可以定义到这个类里面。
  • ConcreteColleague(具体的同事)
    具体的同事类,实现自己的业务,在需要与其它同事通讯的时候,就与持有的中介者通信,中介者会负责与其它的同事交互

这篇文章写的非常详细Mediator模式

Thank you for your support