第19章 行为型模式—中介者模式

1. 中介者模式(Mediator Pattern)的定义

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

     

  ①中介者模式主要用来将同事类之间网状结构变为星状结构,使同事类之间的关系变的清晰一些。

  ②所有对象只跟中介者对象进行通信,相互之间不再有联系,这样也能够集中控制这些对象的交互关系

(2)中介者模式的结构和说明

  ①Mediator: 中介者接口。在里面定义各个同事之间交互需要的方法,可以是公共的通信方法,比如changed方法,大家都可以用(一般会传入同事类的this指针),也可以是小范围的交互方法。

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

  ③Colleague:同事类的定义,通常实现为抽象类,主要负责约束同事对象的类型,并能实现一些具体同事类之间的公共功能。同时,一般会持有中介者的引用

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

【编程实验】班级QQ群

//示意图

    

//UML图

//行为模式——中介者模式
//场景:班级通信

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Colleage; //前向声明

//*******************************************抽象中介者**************************************
//抽象中介者
class Mediator
{
public:
    virtual void addStudent(Colleage* student) = 0;
    //通知
    virtual void notify(Colleage* student) = 0;
    //两个同学私下交流
    virtual void chat(Colleage* student1,Colleage* student2) = 0;
};

//*******************************************抽象同事类**************************************
//抽象同事类
class Colleage
{
private:
    string name;
    string content;
protected:
    Mediator* mediator;
public:
    Colleage(Mediator* mediator, string name="")
    {
        this->mediator = mediator;
        this->name = name;

        this->mediator->addStudent(this);
    }

    string& getName()
    {
        return this->name;
    }

    void setContent(string content)
    {
        this->content = content;
    }

    string& getContent()
    {
        return content;
    }

    virtual void talk() = 0;

    void inform()
    {
        talk();
        mediator->notify(this);
    }
};

//具体的同事类:班长
class Monitor : public Colleage
{
public:
    Monitor(Mediator* mediator, string name=""):Colleage(mediator,name){}

    virtual void talk()
    {
        cout <<"班长 说:" << getContent() << endl;
    }
};

//具体的同事类:团支书
class YouthLeague : public Colleage
{
public:
    YouthLeague(Mediator* mediator, string name=""):Colleage(mediator,name){}

    virtual void talk()
    {
        cout <<"团支书 说:" << getContent() << endl;
    }
};

//具体的同事类:同学A
class StudentA : public Colleage
{
public:
    StudentA(Mediator* mediator, string name=""):Colleage(mediator,name){}

    virtual void talk()
    {
        cout <<"学生A 说:" << getContent() << endl;
    }
};

//具体的同事类:同学A
class StudentB : public Colleage
{
public:
    StudentB(Mediator* mediator, string name=""):Colleage(mediator,name){}

    virtual void talk()
    {
        cout <<"学生B 说:" << getContent() << endl;
    }
};

//具体的中介者(如QQ通讯平台)
class QQMediator: public Mediator
{
private:
    vector<Colleage*> studentList;
public:
    void addStudent(Colleage* student)
    {
        studentList.push_back(student);
    }

    void notify(Colleage* student)
    {
        //student发出通知,其他同学回复
        vector<Colleage*>::iterator iter = studentList.begin();
        while(iter != studentList.end())
        {
            //其他同学的回复
            if(*iter != student)
            {
                (*iter)->talk();
            }
            ++iter;
        }
    }

    //私下交流
    void chat(Colleage* student1,Colleage* student2)
    {
        //学生1说话
        student1->talk();
        //学生2回答
        student2->talk();
    }
};

int main()
{
    //***********************初始化QQ聊天环境********************
    QQMediator qq;
    Monitor mon(&qq,"Minitor");
    YouthLeague youth(&qq,"YouthLeague");
    StudentA stuA(&qq,"StudentA");
    StudentB stuB(&qq, "StudentB");


    //***************班级发通知********************
    mon.setContent("明天下午2点开年段会,收到请回复^^。");
    youth.setContent("知道了,肯定到!!");
    stuA.setContent("收到了,一定准时到!!");
    stuB.setContent("收到了,但明天要去面试,特请假一下!!");

    //开始发通知
    mon.inform();

    //*******************两个同学私下交流**************
    cout << endl << "下面是两个同学的私下交流:" << endl;
    mon.setContent("你觉得咱们地理老师课讲得怎么样?");
    stuA.setContent("我觉得讲的不够生动,还点名,不太好!!!");
    qq.chat(&mon,&stuA);

    return 0;
}

2. 思考中介者模式

(1)中介者模式的本质封装交互。中介者的目的,就是用来封装多个对象的交互,这些交互的处理多在中介者对象里面实现。只要是实现封装对象之间的交互功能,就可用中介者模式,而不必过于拘泥于中介者模式本身的结构。

(2)需要Mediator接口吗

  这取决于是否会提供多个不同的中介者实现。如果中介者实现只有一个的话,而且预计中也没有扩展的需求,那就可以不定义Mediator接口。如果中介者实现不只一个,或者预计有扩展的要求,那么就需要定义Mediator接口。让各个同事类来面向中介者接口编程。

(3)同事关系

  在标准的中介者模式中,将使用中介者对象来交互的那些对象称为同事类,在中介者模式中要求这些类都要继承相同的类,也就是说,这些对象从某个角度来讲是同一个类型,算是兄弟对象。

(4)同事和中介者的关系

  ①当一个同事对象发生了改变,需要主动通知中介者,让中介者去处理与其他同事对象相关的交互。

  ②同事对象需要知道中介者对象是谁,反过来,中介者对象也需要知道相关的同事对象,这样才能与同事对象进行交互。

  ③中介者对象与同事对象之间是相互依赖的。

(5)如何实现同事和中介者的通信

  ①同事类持有中介者对象,可以通过Mediator接口中定义一个特殊的通知接口(如changed),并把this当做参数传入,这样在中介者对象里面,就可以去获取这个同事对象的实例或当中的数据了。中介者对象里面记录着各个同事,会根据从changed接口中传入来的对象,判断下一步的动作。

  ②另一种实现方式可以采用观察者模式,把Mediator实现成为观察者,而各个同事类实现成为Subject,这样同事类发生了改变,会通知Mediator。Mediator在接到通知的以后,会与相应的同事对象进行交互。

【编程实验】用电脑看电影

//行为模式——中介者模式
//场景:使用电脑来看电影
//中介者:主板
//同事类:CPU、内存、光驱、显卡、声卡等

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Colleage; //前向声明

//*******************************************抽象中介者**************************************
//抽象中介者
class Mediator
{
public:
    //同事对象在自身改变的时候调用这个接口来通知中介者
    virtual void changed(Colleage* colleague) = 0;
};

//*******************************************抽象同事类**************************************
//抽象同事类
class Colleage
{
protected:
    Mediator* mediator;
public:
    Colleage(Mediator* mediator)
    {
        this->mediator = mediator;
    }
    //获取当前同事类对应的中介者对象
    Mediator& getMediator()
    {
        return *mediator;
    }
};

//具体的同事类:光驱
class CDDriver : public Colleage
{
private:
    string data;
public:
    CDDriver(Mediator* mediator):Colleage(mediator){}

    //读取光盘
    void readCD()
    {
        //逗号前是视频显示的数据,逗号后是声音
        data = "设计模式,值得好好研究";
        //通知中介者(主板),自己的状态发生了改变
        mediator->changed(this);
    }

    string& getData()
    {
        return data;
    }
};

//具体的同事类:CPU
class CPU : public Colleage
{
private:
    string videoData; //分解出来的视频数据
    string soundData; //分解出来的声音数据
public:
    CPU(Mediator* mediator):Colleage(mediator){}

    string& getVideoData()
    {
        return videoData;
    }

    string& getSoundData()
    {
        return soundData;
    }

    //处理数据,把数据分成音频和视频数据
    void executeData(string data)
    {
        int nPos = data.find(",");
        videoData = data.substr(0,nPos);
        soundData = data.substr(nPos+1,data.length()-nPos);
        //通知主板,CPU的工作完成
        mediator->changed(this);
    }
};

//具体的同事类:显卡类
class VideoCard : public Colleage
{
private:
    string data; //被显示的数据
public:
    VideoCard(Mediator* mediator):Colleage(mediator){}

    //显示数据
    void showData(string data)
    {
        cout << "您正在观看的是:" << data << endl;
    }
};

//具体的同事类:声卡类
class SoundCard : public Colleage
{
private:
    string data; //被播放的声音数据
public:
    SoundCard(Mediator* mediator):Colleage(mediator){}

    //显示数据
    void soundData(string data)
    {
        cout << "画外音:" << data << endl;
    }
};

//*************************************具体中介者****************
//主板类
class MainBoard : public Mediator
{
private:
    CDDriver* cdDriver;
    CPU* cpu;
    VideoCard* videoCard;
    SoundCard* soundCard;

    //处理光驱读取数据以后与其他对象的交互
    void opeCDDriverReadData(CDDriver* cd)
    {
        //1.获取光驱读取的数据
        string data = cd->getData();
        //2.把这些数据传给CPU进行处理
        cpu->executeData(data);
    }

    //CPU处理完数据后与其他对象的交互
    void opeCPU(CPU* cpu)
    {
        //1.先取出CPU处理后的数据
        string& videoData = cpu->getVideoData();
        string& soundData = cpu->getSoundData();

        //2. 把数据传递给显卡和声卡展示出来
        videoCard->showData(videoData);
        soundCard->soundData(soundData);
    }

public:
    void setCDDrriver(CDDriver* cdDriver)
    {
        this->cdDriver = cdDriver;
    }

    void setCPU(CPU* cpu)
    {
        this->cpu = cpu;
    }

    void setVideoCard(VideoCard* videoCard)
    {
        this->videoCard = videoCard;
    }

    void setSoundCard(SoundCard* soundCard)
    {
        this->soundCard = soundCard;
    }

    //接收通知
    void changed(Colleage* colleage)
    {
        //从光驱来的通知
        if(colleage == cdDriver)
        {
            opeCDDriverReadData((CDDriver*)colleage);
        }
        else if(colleage == cpu)
        {
            //表示CPU处理完了
            opeCPU((CPU*)colleage);
        }
    }
};

int main()
{
    //1.创建中介者——主板对象
    MainBoard mediator;
    //2.创建同事类
    CDDriver cd(&mediator);
    CPU cpu(&mediator);
    VideoCard vc(&mediator);
    SoundCard sc(&mediator);

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

    //4.开始看电影,把光盘放入光驱,光驱开始读盘
    cd.readCD();

    return 0;
}

3. 广义的中介者

(1)标准中介者模式的问题

  ①同事对象都要从一个公共的父类继承。在这实际开发中,很多相互交互的对象本身是没有公共父类的,强行加上一个父类,会让这些对象实现起来特别别扭。

  ②同事类必须持有中介者对象吗?在标准的中介者模式中,中介者对象作为属性并通过构造方法注入到同事类中的。而实际开发中,可以把中介者对象做成单例,直接在同事类的方法里面去调用中介者对象,而无须将中介者作为同事类的成员变量。

  ③在实际开发中,很常见的情况是不需要中介者接口的,而且中介者对象也不需要创建多个实例。因为中介者是用来封装和处理同事对象的关系的,它一般被实现为单例。

  ④中介者对象是否需要持有所有的同事?在标准的中介者模式中会将所有的同事类作为成员变量(或保存在链表中)这是一种很强的依赖关系。在实现中,可以在中介者处理的方法里面去创建或获取,或者从参数传入需要的同事对象

  ⑤中介者对象只是提供一个公共的方法来接受同事对象的通知吗?在标准的中介者中只提供一个公共方法,这样还是要去区分到底是哪个同事类发过来的通知。在实际的开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是什么对象,其具体的什么业务了。

(2)广义的中介者模式(也是简化的中介者模式)

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

  ②通常不定义Mediator接口,把具体的中介者对象实现成单例。

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

【编程实验】人事管理

//引入中介者的人事管理示意图

//UML示意图

//行为模式——中介者模式
//场景:人事管理系统
//一个部门可能有多个人,同时一个人也可以加入多个部门,即
//部门和人员是多对多的关系。因此如果人员和部门直接打交道
//那样人员或部门的内部势必要引用多个部门或人员的引用,这样
//耦合性太强,可以把部门和人员的关系放入中介者中,通过中介者
//来维护部门和人员的关系,这样可以将部门和人员解耦。同时集中
//管理人员和部门之间的关系((如删除某个人或部门时的逻辑)

#include <iostream>
#include <string>
#include <vector>

using namespace std;

//**********************辅助类****************
//描述部门和人员关系的类(相当于数据表的一行记录)
class DepUserModel
{
private:
    //用于部门和人员关系的编号
    string depUserId; //用做主键
    string depId;      //部门编号
    string userId;     //人员编号
public:
    string& getDepUserId()
    {
        return depUserId;
    }
    void setDepUserId(string depUserId)
    {
        this->depUserId = depUserId;
    }

    string& getDepId()
    {
        return depId;
    }
    void setDepId(string depId)
    {
        this->depId = depId;
    }

    string& getUserId()
    {
        return userId;
    }

    void setUserId(string userId)
    {
        this->userId = userId;
    }
};

//**********************中介者****************************
//实现部门和人员交互的中介者实现类
class DepUserMediatorImpl
{
private:
    static DepUserMediatorImpl* mediator;

    //构造函数私有化
    DepUserMediatorImpl()
    {
        initTestData(); //初始化测试数据
    }
private:
    vector<DepUserModel*> depUser;

    void initTestData()
    {
        //准备一些测试数据
        DepUserModel* dum = new DepUserModel();
        dum->setDepUserId("du1");
        dum->setDepId("d1");
        dum->setUserId("u1");
        depUser.push_back(dum);

        dum = new DepUserModel();
        dum->setDepUserId("du2");
        dum->setDepId("d1");
        dum->setUserId("u2");
        depUser.push_back(dum);

        dum = new DepUserModel();
        dum->setDepUserId("du3");
        dum->setDepId("d2");
        dum->setUserId("u3");
        depUser.push_back(dum);

        dum = new DepUserModel();
        dum->setDepUserId("du4");
        dum->setDepId("d2");
        dum->setUserId("u4");
        depUser.push_back(dum);

        dum = new DepUserModel();
        dum->setDepUserId("du5");
        dum->setDepId("d2");
        dum->setUserId("u1");
        depUser.push_back(dum);
    }
public:
    //完成因撤销部门的操作所引起的与人员的交互,需要去除相应的关系
    bool deleteDep(string depId)
    {
        //为了演示简单,部门撤销后,原部门的人员怎么处理这里不管了
        //到记录部门和人员关系的集合里面,寻找跟这个部门相关的人员
        vector<DepUserModel*>::iterator iter = depUser.begin();
        while(iter != depUser.end())
        {
            if( (*iter)->getDepId() == depId)
            {
                delete (*iter);
                iter = depUser.erase(iter);
            }
            else
                ++iter;
        }
        return true;
    }

    //完成因人员离职引起的与部门交互
    bool deleteUser(string userId)
    {
        //到记录部门和人员关系的集合里面,寻找跟这个人员相关
        //的部门,并删除那些记录。
        vector<DepUserModel*>::iterator iter=depUser.begin();
        while(iter != depUser.end())
        {
            if((*iter)->getUserId() == userId)
            {
                delete (*iter);
                iter = depUser.erase(iter);
            }
            else
                ++iter;
        }
        return true;
    }

    //完成因人员调换部门引起的与部门交互
    bool changeDep(string userId,string oldDepId,string newDepId)
    {
        //本例不去实现了
        return false;
    }

    //完成因部门合并操作所引起的与人员的交互
    //@param depIds 需要合并的部门编号的集合
    //@param newDep 合并后新的部门对象
    bool joinDep(vector<string> depIds, string** newDepId)
    {
        //本例不去实现了
        return false;
    }

    //测试用,在内部打印显示一个部门下的所有人员
    void showDepUsers(string depId)
    {
        vector<DepUserModel*>::iterator iter=depUser.begin();
        while(iter != depUser.end())
        {
             if((*iter)->getDepId() == depId)
             {
                  cout <<"部门编号=" << depId
                       <<"下面拥有人员,其编号是:"
                       <<(*iter)->getUserId() << endl;
             }
             ++iter;
        }
    }

    //测试用,在内部打印显示一个人员所属的部门
    void showUserDeps(string userId)
    {
        vector<DepUserModel*>::iterator iter=depUser.begin();
        while(iter != depUser.end())
        {
             if((*iter)->getUserId() == userId)
             {
                  cout <<"人员编号=" << userId
                       <<"所属的部门编号是:"
                       <<(*iter)->getDepId() << endl;
             }
             ++iter;
        }
    }

public:
    static DepUserMediatorImpl* getInstance()
    {
        if(mediator == NULL)
            mediator = new DepUserMediatorImpl();

        return mediator;
    }
};

DepUserMediatorImpl* DepUserMediatorImpl::mediator = NULL;

//部门类
class Dep
{
private:
    string depId;   //部门编号
    string depName; //部门名称
public:
    void setDepId(string depId)
    {
        this->depId = depId;
    }

    string& getDepId()
    {
        return depId;
    }

    void setDepName(string depName)
    {
        this->depName = depName;
    }

    string& getDepName()
    {
        return depName;
    }

    //撤销部门
    bool deleteDep()
    {
        //1.部门类只与中介打交通,先通过中介者去除掉所有与这个部门相关的部门和人员的关系
        DepUserMediatorImpl* mediator = DepUserMediatorImpl::getInstance();
        mediator->deleteDep(depId);

        //2.真正删除掉这个部门

        return true;
    }
};

//人员类
class User
{
private:
    string userId;
    string userName;
public:
    string& getUserId()
    {
        return userId;
    }

    void setUserId(string userId)
    {
        this->userId = userId;
    }

    string& getUserName()
    {
        return userName;
    }

    void setUserName(string userName)
    {
        this->userName = userName;
    }

    //人员离职
    bool dimission()
    {
        //1.人员类只与中介打交通,先通过中介者去除掉所有与这个人员相关的部门和人员的关系
        DepUserMediatorImpl* mediator = DepUserMediatorImpl::getInstance();
        mediator->deleteUser(userId); //离职

        //2.然后删除这个人员(注意,实际开发中,是不会真正删除人员记录的。

        return true;
    }

};

int main()
{
    DepUserMediatorImpl& mediator = *(DepUserMediatorImpl::getInstance());

    //准备要撤销的部门
    Dep dep;
    dep.setDepId("d1");
    Dep dep2;
    dep2.setDepId("d2");

    //准备用于测试的人员
    User user;
    user.setUserId("u1");


    //测试撤销部门,在运行之前,输出一下,看这个人员属于哪些部门
    cout <<"撤销部门前-----------------------------" << endl;
    mediator.showUserDeps(user.getUserId());

    //真正执行业务撤销这个部门
    dep.deleteDep();

    //再次输出一下,看这个人员属于哪些部门
    cout <<"撤销部门后-----------------------------" << endl;
    mediator.showUserDeps(user.getUserId());

    //测试人员离职,在运行之前,输出一下,看这个部门下都有哪些人员
    cout <<"人员离职前-----------------------------" << endl;
    mediator.showDepUsers(dep2.getDepId());

    //真正执行业务,人员离职
    user.dimission();

    //再次输出一下,看这个人员属于哪些部门
    cout <<"人员离职后-----------------------------" << endl;
    mediator.showDepUsers(dep2.getDepId());

    return 0;
}
/*输出结果:
撤销部门前-----------------------------
人员编号=u1所属的部门编号是:d1
人员编号=u1所属的部门编号是:d2
撤销部门后-----------------------------
人员编号=u1所属的部门编号是:d2
人员离职前-----------------------------
部门编号=d2下面拥有人员,其编号是:u3
部门编号=d2下面拥有人员,其编号是:u4
部门编号=d2下面拥有人员,其编号是:u1
人员离职后-----------------------------
部门编号=d2下面拥有人员,其编号是:u3
部门编号=d2下面拥有人员,其编号是:u4
*/

4. 中介者模式的优缺点

(1)优点

  ①松散耦合:中介者把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互不依赖。这样同事对象可以独立变化和复用。

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

  ③多对多变一对多的关系

(2)缺点

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

  ②由于“中介“承担了较多的责任,所以一旦这个中介对象出现了问题,那么整个系统就会受到重大的影响。

5. 中介者的应用场景

(1)同事类之间是网状结构的关系,可以考虑使用中介者模式。它会将网状结构变为星状结构,使同事类之间的关系变的清晰一些。

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

6. 相关模式

(1)中介者模式与外观模式

  ①外观模式多用来封装一个子系统内部的多个模式,目的是向子系统外部提供简单易用的接口。也就是说外观模式封装的是子系统外部和子系统内部模块间的交互。而中介者模式是提供多个平等的同事对象之间交互关系的封装,一般是用在内部实现上。

  ②外观模式的实现是单向的交互,是从子系统外部来调用子系统内部,不会反着过来;而中介者模式实现是内部多个模块间多向的交互

(2)中介者模式和观察者模式

  中介者模式可以结合观察者模式来实现当同事对象发生改变的时候,通知中介对象,让中介对象去进行与其他相关对象的交互。

猜你喜欢

转载自blog.csdn.net/CherishPrecious/article/details/84022309