分享
 
 
 

大卫的Design Patterns学习笔记17:Mediator

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

一、概述

Mediator(中介者)模式的名称已经基本能够反映该模式的意图:用一个中介对象来封装一系列的对象之间的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。Mediator很象十字路口的红绿灯,每个车辆只需和红绿灯交互,而不是在各车辆之间进行交互。

Mediator与Proxy的比较

Proxy模式是简化的Mediator模式?

个人认为从功能上讲,Mediator模式与Proxy模式存在一定的相似性,但Proxy主要在于控制对被访问对象的访问,这种访问往往是单向的,而Mediator模式则在于为系统内不同的对象之间的访问提供一种媒介,这种访问往往是多向的。

Mediator与Facade的比较

很多设计模式的书中都会将Mediator模式与Facade模式进行比较,个人认为二者的区别十分明显:一个是为整个子系统对外提供一个简单化的接口,子系统的内部可能十分复杂,很多类交互作用才形成了最终的简单化的统一接口;而Mediator模式则作为系统内多个对象交互的“媒介”,负责封装对象间的交互,所有对象只需要与Mediator类交互,而不是相互之间直线联系。所以,也许在实现Facade模式时在子系统的内部采用Mediator模式可能是个不错的选择。

二、结构

Mediator模式的结构如下图所示:

图1、Mediator模式类图示意

上面的类图并不能完全反映Mediator模式的全部思想,下面结合GoF的DP一书中的一个对象图对此进行深入说明:

图2、一个典型的使用Mediator模式的对象图

在上面的类图中,各角色的分工如下:

1、Mediator(中介者):中介者定义一个接口用于与各Colleague(同事)对象通信。

2、ConcreteMediator(具体中介者):具体中介者通过协调各同事对象实现协作行为,并了解和维护它的各个同事。

3、Colleague class(同事类):每一个同事类都知道它的中介者对象,每一个同事对象在需与其他的同事通信的时候,与它的中介者通信。

三、应用

各个对象之间的交互操作非常多,每个对象的行为操作都依赖彼此对方,修改一个对象的行为,同时会涉及到修改很多其他对象的行为,如果使用Mediator模式,可以使各个对象间的耦合松散,只需关心和 Mediator的关系,使多对多的关系变成了一对多的关系,可以降低系统的复杂性,提高可修改扩展性。

在下列情况下使用中介者模式:

1、一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。

2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。

3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。

四、优缺点

Mediator模式有以下优点和缺点:

1、减少了子类生成Mediator将原本分布于多个对象间的行为集中在一起,改变这些行为只需生成Mediator的子类即可,这样各个Colleague类可被重用。

2、它将各Colleague解耦。Mediator有利于各Colleague间的松耦合,你可以独立的改变和复用各Colleague类和Mediator类。

3、它简化了对象协议用Mediator和各Colleague间的一对多的交互来代替多对多的交互。一对多的关系更易于理解、维护和扩展。

4、它对对象如何协作进行了抽象将中介作为一个独立的概念并将其封装在一个对象中,使你将注意力从对象各自本身的行为转移到它们之间的交互上来。这有助于弄清楚一个系统中的对象是如何交互的。

5、它使控制集中化中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能变得比任一个Colleague都复杂。这可能使得中介者自身成为一个难于维护的庞然大物。

五、举例

Mediator模式是GoF的23种设计模式中较为容易理解的一个,我们在平时的应用中也会经常用到,但可能不会有意去抽象出一个完整的Mediator类,因为要设计一个可复用又可扩展的Mediator是很不容易的。如在MFC的文档/视图程序中,我们可能会在CWinApp派生类中添加一些供所有线程共享的数据成员或方法,这样,各线程即可通过与theApp进行交互即可达到影响其它线程的目的,在这里,CWinApp一定程度上扮演了Mediator的角色。

有人称MFC程序中Dialog在各控件间扮演着Mediator的角色,认为各控件实现时无需知道其它控件,只是将消息发送给父窗口Dialog来处理,而Dialog负责在消息处理函数中完成各控件之间的信息交互,使得控件间复杂的耦合关系变成简单的控件-Mediator(Dialog)关系。我并非十分赞同这种观点,因为我们往往将控件作为整个Dialog的一个组成部分来看待,而有意无意地忽略了它本身也是一个独立对象的事实,造成这种现象的一个关键原因可能在于MFC的消息映射机制屏蔽了控件将消息发送给对话框的过程,使得我们更愿意去认为消息是Dialog(处理)的消息,而不是(来自)控件的消息。

在Java应用中,以上状况依然存在,但是由于没有了直接的消息映射支持,JDialog往往更容易表现得像一个Mediator。

此外,还有一个观点认为,Java的Container通过选择一定的LayoutManager策略(Strategy,后面的笔记中将讲到),在各控件间起到了Mediator的作用,当新的Component被加到Container,如JPanel中时,必然会对其它控件的Layout(布局)造成影响,但各控件间是不知道对方的,他们只需知道自己的Parent Window,然后与之交互即可。这种观点也有欠妥当,但从Mediator作为各object之间的交互的媒介这一实质来看,也无可厚非。

简而言之,Mediator模式的主要作用在于为多个对象之间的交互提供一种媒介,以简化对象之间的耦合关系。下面用两个实例来说明Mediator模式的应用:

1、ChatRoom:

聊天室大家想必都知道,在下面的示例中,我们尝试实现一个布告栏式的ChatRoom,任何人想要发送消息给其他人,只需将消息发送给ChatRoom对象,由ChatRoom负责数据的转发,而不是直接与消息的接收者交互。后面的笔记中将从Observer模式的角度来解决ChatRoom问题。

#include <iostream>

#include <string>

#include <map>

using namespace std;

class Participant;

// "AbstractMediator"

struct IChatroom

{

// Methods

virtual void Register( Participant* participant ) = 0;

virtual void Send( string& from, string& to, string& message ) = 0;

};

// "AbstractColleague"

class Participant

{

friend class Chatroom;

// Fields

private:

IChatroom* pChatroom;

string name;

// Constructors

public:

/*Participant()

{

}*/

Participant( const char* name )

{

this->name = name;

}

virtual ~Participant()

{

}

// Methods

virtual void Send( string& to, string& message )

{

pChatroom->Send( name, to, message );

}

virtual void Receive( string& from, string& message )

{

cout << from.c_str() << " to " << name.c_str() << " : [" << message.c_str() << "]" << endl;

}

};

// More ConcreteColleague, omitted...

// "ConcreteMediator"

class Chatroom : public IChatroom

{

// Fields

private:

map<string, Participant*> mapParticipants; // nickname to Participant map

// Methods

public:

void Register( Participant* pParticipant )

{

mapParticipants[ pParticipant->name ] = pParticipant;

pParticipant->pChatroom = this;

}

void Send( string& from, string& to, string& message )

{

map<string, Participant*>::iterator ptr;

ptr = mapParticipants.find(to);

if ( ptr != mapParticipants.end() )

{

Participant* pto = (*ptr).second;

pto->Receive( from, message );

}

}

};

int main()

{

// Create chatroom

Chatroom c;

// Create 'chatters' and register them

Participant George("George");

Participant Paul("Paul");

Participant Ringo("Ringo");

c.Register( &George );

c.Register( &Paul );

c.Register( &Ringo );

// Chatting participants

George.Send( string("Paul"), string("Hi Paul!") );

Paul.Send( string("Ringo"), string("Good Morning!") );

Ringo.Send( string("George"), string("Hi Friend!") );

return 0;

}

2、多线程Producer-Consumer:

以下是一个多线程Producer-Comsumer的例子,在该示例中,由于无法在多个Producer、Cosumer之间建立直接的联系,因此,通过Mediator类来完成这种信息交互,当Producer要Produce时,只需与Mediator进行交互,以查询是否有空的Slot可供存放Product,而Comsumer要Comsume时,也只需与Mediator进行交互,以查询是否有Product可供Comsume。

以下是该示例的Java实现:

import java.util.*;

class Product {

int id;

Product(int id) {

this.id = id;

}

}

class Mediator {

private boolean stopFlag = false;

private Stack slot = new Stack();

private int slotCount;

public Mediator(int slotCount) {

this.slotCount = slotCount;

}

public boolean stop() {

return stopFlag;

}

public void stop(boolean flag) {

stopFlag = true;

}

public boolean put(Product product) {

synchronized( slot ) { // or synchronized on Mediator.class, but on slot is better and reasonable

if ( slot.size() >= slotCount ) {

return false;

}

slot.push( product );

}

return true;

}

public Product get() {

synchronized( slot ) {

if ( slot.empty() )

return null;

Product product = (Product)slot.pop();

return product;

}

}

}

class Producer extends Thread {

private Mediator med;

private int id;

private static int num = 1;

public Producer(Mediator m) {

med = m;

id = num++;

}

public void run() {

Product product;

while ( !med.stop() ) {

product = new Product((int) (Math.random() * 100));

synchronized (System.out) {

System.out.println("Producer[" + id + "] produces Product["

+ product.id + "]");

}

while ( !med.stop() && !med.put(product) ) { // if put failed, try to put again and again.

try {

sleep( 100 );

} catch (InterruptedException ie) {

}

}

try {

sleep( 100 );

} catch (InterruptedException ie) {

}

}

}

}

class Consumer extends Thread {

private Mediator med;

private int id;

private static int num = 1;

public Consumer(Mediator m) {

med = m;

id = num++;

}

public void run() {

Product product;

while ( !med.stop() ) {

product = med.get();

if ( product != null ) {

synchronized (System.out) {

System.out.println("Consumer[" + id + "] is consuming Product["

+ product.id + "]");

}

}

try {

sleep( 100 );

} catch (InterruptedException ie) {

}

}

}

}

class MediatorDemo {

public static void main(String[] args) {

Mediator med = new Mediator(2);

Thread thread[] = { new Producer(med), new Producer(med),

new Consumer(med), new Consumer(med), new Consumer(med) };

for (int i = 0; i < thread.length; i++)

thread[i].start();

// before stop all threads, sleep 1 second

try {

Thread.sleep(1000);

} catch (InterruptedException ie) {

}

med.stop(true);

// Wait for all threads to return

try {

for (int i = 0; i < thread.length; i++) {

thread[i].join();

}

} catch (InterruptedException ie) {

}

}

}

由于JDK为Java程序员提供了丰富的线程及线程同步支持,因此,要用Java解决上述问题,是比较简单的,但是C++程序员就没那么幸运了,因为C++到目前为止并没有规范线程支持(据说以后会有,但是按照C++标准n年更新一次的速度,谁知道什么时候会有,好在我们不用等了,在一些关注应用的库中已经能够提供了一些十分稳定的Thread支持)。

下面是上述示例的等价C++实现,代码中使用了Boost.Thread(关于如何在VC下使用boost::thread,见参考3;关于如何在VC6下编译boost,见参考4,VC2003/VC2005的用户请自行相应修改)。如果你不想使用boost,可以将其中的thread换成等价的其它线程库实现中的等价类,如log4cxx::helpers::Thread、ACE_Thread/ACE_Thread_Manager,或者参考3中的跨平台线程库等,当然,MFC的CWinThread对于VC++开发人员应该是最简便的选择。

虽然使用了boost::thread,但由于缺少语言本身的支持(如果C++能像Java一样提供synchronized关键字,实现会简化很多),下面的实现仍然显得有点臃肿,你在读这个例子时如果对boost::thread的使用不感兴趣,完全可以将它想象成一个普通的类,无需考虑具体的使用细节,只要明白其语义及整个代码所表现的Mediator模式的实质即可。

#include <boost/thread/thread.hpp>

#include <boost/thread/mutex.hpp>

#include <boost/thread/condition.hpp>

#include <boost/thread/xtime.hpp>

#include <iostream>

#include <time.h> // for time()

#include <Windows.h> // for Sleep, change it for other platform

typedef boost::mutex::scoped_lock scoped_lock;

boost::mutex io_mutex;

class Product

{

int num;

public:

Product(int num) : num(num) {}

friend std::ostream& operator<< (std::ostream& os, Product& product)

{

return os << product.num;

}

};

class Mediator

{

private:

boost::condition cond;

boost::mutex mutex;

Product** pSlot; // product buffer/slot

unsigned int slotCount, // buffer size

productCount; // current product count

bool stopFlag; // should all thread stop or not

public:

Mediator(const int slotCount) : slotCount(slotCount), stopFlag(false), productCount(0)

{

pSlot = new Product*[slotCount];

}

virtual ~Mediator()

{

for (int i = 0; i < static_cast<int>(productCount); i++)

{

delete pSlot[i];

}

delete [] pSlot;

}

bool Stop() const { return stopFlag; }

void Stop(bool) { stopFlag = true; }

void NotifyAll()

{

cond.notify_all();

}

bool Put( Product* pProduct)

{

scoped_lock lock(mutex);

if (productCount == slotCount)

{

{

scoped_lock lock(io_mutex);

std::cout << "Buffer is full. Waiting..." << std::endl;

}

while (!stopFlag && (productCount == slotCount))

cond.wait(lock);

}

if (stopFlag) // it may be notified by main thread to quit.

return false;

pSlot[ productCount++ ] = pProduct;

cond.notify_one(); // this call may cause *pProduct to be changed if it wakes up a consumer

return true;

}

bool Get(Product** ppProduct)

{

scoped_lock lock(mutex);

if (productCount == 0)

{

{

scoped_lock lock(io_mutex);

std::cout << "Buffer is empty. Waiting..." << std::endl;

}

while (!stopFlag && (productCount == 0))

cond.wait(lock);

}

if (stopFlag) // it may be notified by main thread to quit.

{

*ppProduct = NULL;

return false;

}

*ppProduct = pSlot[--productCount];

cond.notify_one();

return true;

}

};

class Producer

{

private:

Mediator* pMediator;

static unsigned int num;

unsigned int id; // Producer id

public:

Producer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }

void operator() ()

{

Product* pProduct;

srand( (unsigned)time( NULL ) + id ); // each thread need to srand differently

while (!pMediator->Stop())

{

pProduct = new Product( rand() % 100 );

// must print product info before call Put, as Put may wake up a consumer

// and cause *pProuct to be changed

{

scoped_lock lock(io_mutex);

std::cout << "Producer[" << id << "] produces Product["

<< *pProduct << "]" << std::endl;

}

if (!pMediator->Put(pProduct)) // this function only fails when it is notified by main thread to exit

delete pProduct;

Sleep(100);

}

}

};

unsigned int Producer::num = 1;

class Consumer

{

private:

Mediator* pMediator;

static unsigned int num;

unsigned int id; // Consumer id

public:

Consumer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }

void operator() ()

{

Product* pProduct = NULL;

while (!pMediator->Stop())

{

if (pMediator->Get(&pProduct))

{

scoped_lock lock(io_mutex);

std::cout << "Consumer[" << id << "] is consuming Product["

<< *pProduct << "]" << std::endl;

delete pProduct;

}

Sleep(100);

}

}

};

unsigned int Consumer::num = 1;

int main()

{

Mediator mediator(2); // we have only 2 slot to put products

// we have 2 producers

Producer producer1(&mediator);

boost::thread thrd1(producer1);

Producer producer2(&mediator);

boost::thread thrd2(producer2);

// and we have 3 consumers

Consumer consumer1(&mediator);

boost::thread thrd3(consumer1);

Consumer consumer2(&mediator);

boost::thread thrd4(consumer2);

Consumer consumer3(&mediator);

boost::thread thrd5(consumer3);

// wait 1 second

Sleep(1000);

// and then try to stop all threads

mediator.Stop(true);

mediator.NotifyAll();

// wait for all threads to exit

thrd1.join();

thrd2.join();

thrd3.join();

thrd4.join();

thrd5.join();

return 0;

}

参考:

1、http://www.dofactory.com/Patterns/PatternMediator.aspx

2、http://home.earthlink.net/~huston2/dp/MediatorDemosJava

3、http://blog.vckbase.com/billdavid/archive/2005/03/07/3412.html

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有