行为型模式—中介者模式
Posted 浅墨浓香
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了行为型模式—中介者模式相关的知识,希望对你有一定的参考价值。
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() { <以上是关于行为型模式—中介者模式的主要内容,如果未能解决你的问题,请参考以下文章