20. 中介者模式
一、中介者模式
如果在一个系统中对象之间的联系呈现为网状结构。对象之间存在大量的多对多联系,将导致系统非常复杂,这些对象既会影响别的对象,也会被别的对象所影响。在网状结构中,几乎每个对象都需要与其他对象发生相互作用,而这种相互作用表现为一个对象与另外一个对象的直接耦合,这将导致一个过度耦合的系统。
中介者模式可以使对象之间的关系数量急剧减少。通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构。在这个星形结构中,一个对象不再直接与另一个对象联系,它通过中介者对象与另一个对象发生相互作用。中介者对象的存在保证了对象结构上的稳定,也就是说,系统的结构不会因为新对象的引入带来大量的修改工作。
中介者模式(Mediator Pattern)用一个中介对象(中介者)来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。这种模式又被称为调停模式,是迪米特法则的典型应用。在中介者模式中,各个对象通过中介者进行通信,从而减少了它们之间的依赖关系。
中介者模式中常用的角色如下:
- 抽象中介者(Mediator):它定义一个接口,该接口用于与各同事对象之间进行通信,提供了同事对象注册与转发同事对象的抽象方法。
- 具体中介者(ConcreteMediator):它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,维持了对各个同事对象的引用。
- 抽象同事类(Colleague):它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。
- 具体同事类(ConcreteColleague):它是抽象同事类的子类。每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信。在具体同事类中实现了在抽象同事类中声明的抽象方法。
中介者模式的核心在于 中介者类 的引入。在中介者模式中,中介者类 承担了以下两方面的职责:
- 中转作用(结构性)。通过中介者提供的中转作用,各个同事对象就不再需要显式地引用其他同事。当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。
- 协调作用(行为性)。中介者可以更进一步地对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做。中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
总之,中介者模式通过引入中介者对象来管理对象之间的交互,降低了系统的复杂性,提高了系统的可维护性和可扩展性。然而,在使用中介者模式时,也需要注意避免中介者变得过于复杂和难以维护。
二、C++实现中介者模式
在抽象中介者中定义一个接口,该接口用于与各同事对象之间进行通信,提供了同事对象注册与转发同事对象的抽象方法。
// 抽象中介者
class Mediator
{
public:
virtual void contact(Colleague * person, std::string message) = 0;
};
在具体中介者类中将实现这些抽象方法。在具体中介者类中将调用同事类的方法,调用时可以增加一些自己的业务代码对调用进行控制。
// 具体中介者
class ConcreteMediator : public Mediator
{
private:
HouseOwner * houseOwner;
Teant * teant;
public:
void contact(Colleague * person, std::string message) override;
HouseOwner * getHouseOwner(void);
void setHouseOwner(HouseOwner * houseOwner);
Teant * getTeant(void);
void setTeant(Teant * teant);
};
void ConcreteMediator::contact(Colleague * colleague, std::string message)
{
if (colleague == houseOwner)
{
teant->receiveMessage(colleague, message);
}
else if (colleague == teant)
{
houseOwner->receiveMessage(colleague, message);
}
}
HouseOwner * ConcreteMediator::getHouseOwner(void)
{
return this->houseOwner;
}
void ConcreteMediator::setHouseOwner(HouseOwner * houseOwner)
{
this->houseOwner = houseOwner;
}
Teant * ConcreteMediator::getTeant(void)
{
return this->teant;
}
void ConcreteMediator::setTeant(Teant * teant)
{
this->teant = teant;
}
在抽象同事类中维持了一个抽象中介者的引用,用于调用中介者的方法。在抽象同事类中声明了同事类的抽象方法,而在具体同事类中将实现这些方法。
// 抽象同事类
class Colleague
{
private:
std::string name;
Mediator * mediator; // 抽象中介者的引用
public:
Colleague(std::string name);
virtual void receiveMessage(Colleague * other, std::string message);
virtual void SendMessage(std::string message);
std::string getName(void);
void setName(std::string name);
Mediator * getMediator(void);
void setMediator(Mediator * mediator);
};
void Colleague::SendMessage(std::string message)
{
getMediator()->contact(this, message);
}
void Colleague::receiveMessage(Colleague * other, std::string message)
{
std::cout << "【" << getName() << "】接收到来自【" << other->getName() << "】信息:" << message << std::endl;
}
Colleague::Colleague(std::string name) : name(name) {}
std::string Colleague::getName(void)
{
return name;
}
void Colleague::setName(std::string name)
{
this->name = name;
}
Mediator * Colleague::getMediator(void)
{
return mediator;
}
void Colleague::setMediator(Mediator * mediator)
{
// 设置中介者时,将同事类添加到中介者中同事类集合中
this->mediator = mediator;
}
在具体同事类中实现了在抽象同事类中声明的方法。
// 具体同事类
class HouseOwner : public Colleague
{
public:
using Colleague::Colleague;
};
// 具体同事类
class Teant : public Colleague
{
public:
using Colleague::Colleague;
};
main() 函数:
int main(void)
{
ConcreteMediator * concreteMediator = new ConcreteMediator(); // 中介者
HouseOwner * houseOwmer = new HouseOwner("艾尔海森"); // 房东
houseOwmer->setMediator(concreteMediator); // 设置中介者
Teant * teant = new Teant("卡维"); // 租客
teant->setMediator(concreteMediator);
concreteMediator->setHouseOwner(houseOwmer); // 中介者关联房东
concreteMediator->setTeant(teant); // 中介者关联租客
teant->SendMessage("我破产了,你要收留我。");
houseOwmer->SendMessage("好吧,希望你半夜不要敲敲打打做模型。");
delete concreteMediator;
delete houseOwmer;
delete teant;
return 0;
}
三、中介者模式的总结
中介者模式将一个网状的系统结构变成一个以中介者对象为中心的星形结构。在这个星形结构中,使用中介者对象与其他对象的一对多关系来取代原有对象之间的多对多关系。
在中介者模式的实际使用过程中,如果需要引入新的具体同事类,只需要继承抽象同事类并实现其中的方法即可。由于具体同事类之间并无直接的引用关系,因此原有所有同事类无须进行任何修改,它们与新增同事对象之间的交互可以通过修改或者增加具体中介者类来实现。如果需要在原有系统中增加新的具体中介者类,只需要继承抽象中介者类(或已有的具体中介者类)并覆盖其中定义的方法即可。在新的具体中介者中可以通过不同的方式来处理对象之间的交互,也可以增加对新增同事的引用和调用。在客户端中只需要修改少许代码(如果引入配置文件的话有时可以不修改任何代码)就可以实现中介者的更换。
3.1、中介者模式的优点
- 中介者模式简化了对象之间的交互,它用中介者和同事的一对多交互代替了原来同事之间的多对多交互。一对多关系更容易理解、维护和扩展,将原本难以理解的网状结构转换成相对简单的星形结构。
- 中介者模式可将各同事对象解耦。中介者有利于各同事之间的松耦合,可以独立地改变和复用每一个同事和中介者,增加新的中介者和新的同事类都比较方便,更好地符合开闭原则。
- 可以减少大量同事子类生成。中介者将原本分布于多个对象间的行为集中在一起,改变这些行为只需要生成新的中介者子类即可,这使得各个同事类可以被重用,无须对同事类进行扩展。
3.2、中介者模式的缺点
在具体中介者类中包含了大量同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。
3.3、中介者模式的适用场景
- 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。
- 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。
- 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。可以通过引入中介者类来实现,在中介者中定义对象交互的公共行为,如果需要改变行为则可以增加新的具体中介者类。