本文记录中介者模式。
中介者(Mediator)模式还有一个名字叫作调停者模式,是一种行为型模式。

中介者的举例

计算机的各个组成部件,都通过主板通信。
qq聊天:与好友或者陌生人单独聊天(私聊);加入到一个qq群中,这样所有人都能看到消息。
飞机的安全飞行与安全着陆:塔台,塔台指挥各个飞机。

中介者模式范例的引入

下面的例子中,设置了账号登录后,界面出现的账号登录相关的按钮;如果选择了游客登录后,界面显示的游客登录相关的按钮。
下面的账号登录的功能。
在这里插入图片描述

//UI控件类的父类
class CWidget
{
public:
	CWidget(string caption) :m_caption(caption) {} //构造函数
	virtual ~CWidget() {} //做父类时析构函数应该为虚函数

public:
	virtual void Changed(map<string, CWidget*>& ctlList) = 0;
	virtual void Enable(bool sign) = 0;
protected:
	string m_caption;	// 控件上的描述文字
};

// 
class Button : public CWidget
{
public:
	Button(string caption) : CWidget(caption) {} //构造函数

	virtual void Enable(bool sign)
	{
		if (sign == true)
		{
			cout << "按钮\"" << m_caption << "\"被设置为了\"启用\"状态" << endl;
		}
		else
		{
			cout << "按钮\"" << m_caption << "\"被设置为了\"禁用\"状态" << endl;
		}
		//......具体实现按钮启用或者禁用的代码。。。。
	}

	/* 按钮被按下时,该成员函数会被调用 */
	virtual void Changed(map<string, CWidget*>& ctlList)
	{
		if (m_caption == "登录")
		{
			cout << "登录按钮按下了,如果是游客登录直接加载本地存档,如果是账号密码登录则验证账号密码" << endl;
		}
		else if (m_caption == "退出")
		{
			cout << "游戏退出, 再见" << endl;
		}
	}
};

// 单选按钮相关类
class RadioBtn : public CWidget
{
public:
	RadioBtn(string caption) : CWidget(caption) {} //构造函数

public:
	virtual void Changed(map<string, CWidget*>& ctlList)
	{
		// 如果是游客登录,账号密码框禁用,登录框启用
		if (m_caption == "游客登录")
		{
			static_cast<RadioBtn *>(ctlList["游客登录"])->Selected(true); // 设置单选为选中状态
			static_cast<RadioBtn *>(ctlList["账号登录"])->Selected(false); // 设置单选为未选中状态
			static_cast<Button *>(ctlList["登录"])->Enable(true); // 启用登录按钮
			ctlList["游客登录"]->Enable(false); // 启用游客登录
			ctlList["账号登录"]->Enable(false); // 启用账号登录
		}
		else if (m_caption == "账号登录")
		{
			ctlList["账号"]->Enable(true); // 启用账号
			ctlList["密码"]->Enable(true); // 启用密码
			static_cast<RadioBtn*>(ctlList["游客登录"])->Selected(false); // 设置单选为未选中状态
			static_cast<RadioBtn*>(ctlList["账号登录"])->Selected(true); // 设置单选为选中状态
		}
	}
	virtual void Enable(bool sign) {}

	// 添加一个单选,选中中间有一个黑色的实心圆点
	void Selected(bool sign)
	{
		if (sign == true)
		{
			cout << "单选按钮\"" << m_caption << "\"被选中" << endl;
		}
		else
		{
			cout << "单选按钮\"" << m_caption << "\"被取消选中" << endl;
		}
	}
};

// 编辑框:账号密码
class EditCtl : public CWidget
{
public:
	EditCtl(string caption) : CWidget(caption) {} //构造函数

	// 编辑框的启动或者禁用
	virtual void Enable(bool sign)
	{
		if (sign == true)
		{
			cout << "编辑框 " << m_caption << " 被设置为了\"启用\"状态" << endl;
		}
		else
		{
			cout << "编辑框" << m_caption << " 被设置为了\"禁用\"状态" << endl;
		}
	}

	// 设置编辑框的内容
	bool isContentEmpty()
	{
		return m_caption.empty();
	}

	// 编辑框的内容发生变化时,该成员函数会被调用
	virtual void Changed(map<string, CWidget*>& ctlList)
	{
		if (static_cast<EditCtl*>(ctlList["账号"])->isContentEmpty() || static_cast<EditCtl*>(ctlList["账号"])->isContentEmpty())
		{
			static_cast<EditCtl*>(ctlList["登录"])->Enable(false);
		}
		else
		{
			static_cast<EditCtl*>(ctlList["登录"])->Enable(true);
		}
	}	
};

// 图像文件格式:根据文件名,读取数据到缓冲区中
void test()
{
	map<string,CWidget*> ctlList;
	ctlList["登录"] = new Button("登录");
	ctlList["退出"] = new Button("退出");
	ctlList["游客登录"] = new RadioBtn("游客登录");
	ctlList["账号登录"] = new RadioBtn("账号登录");
	ctlList["账号"] = new EditCtl("账号");
	ctlList["密码"] = new EditCtl("密码");

	// 账号登录
	ctlList["账号登录"]->Changed(ctlList);
	/*
		编辑框 账号 被设置为了"启用"状态
		编辑框 密码 被设置为了"启用"状态
		单选按钮"游客登录"被取消选中
		单选按钮"账号登录"被选中
	*/

	// 释放资源
	for (auto it = ctlList.begin(); it != ctlList.end(); it++)
	{
		delete it->second;
	}
}

引入中介者模式

定义:用一个中介对象(中介者)来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。
将与客户交互的代码放在中介者类中实现。

	class CWidget;

	// 中介者类
	class CMediator
	{
	public:
		virtual ~CMediator()
		{
		}

	public:
		virtual void createWidget() = 0;		// 创建所有需要用到的UI控件
		virtual void WidgetChanged(CWidget* theChangedWidget) = 0; // 当某个UI 发生变化,调用对应的类做改变
	};

	// 控件父类
	class CWidget
	{
	public:
		CWidget(CMediator* mediator, string caption) : m_pmediator(mediator), m_caption(caption)
		{
		}

		//当UI控件发生变化时该成员函数会被调用
		virtual void Changed()
		{
			m_pmediator->WidgetChanged(this); //将自身传递进去,通知中介者对象,所有事情让中介者对象去做
		}

		//设置UI控件的启用或禁用
		virtual void Enable(bool sign) = 0;
	protected:
		CMediator* m_pmediator;
		string	  m_caption;
	};

	// 普通按钮类
	class CButtonWidget : public CWidget
	{
	public:
		CButtonWidget(CMediator* mediator, string caption) : CWidget(mediator, caption) {}

		virtual void Enable(bool sign)
		{
			if (sign == true)
			{
				cout << "按钮\"" << m_caption << "\"被激活" << endl;
			}
			else
			{
				cout << "按钮\"" << m_caption << "\"被禁用" << endl;
			}
		}
	};

	// 单选按钮类
	class CRadioButtonWidget : public CWidget
	{
	public:
		CRadioButtonWidget(CMediator* ptmpm, string caption) : CWidget(ptmpm, caption) {}

		// 设置按钮启动或者禁用
		virtual void Enable(bool sign)
		{
			if (sign == true)
			{
				cout << "单选按钮\"" << m_caption << "\"被激活" << endl;
			}
			else
			{
				cout << "单选按钮\"" << m_caption << "\"被禁用" << endl;
			}
		}

		// 设置单选按钮被选中
		void Selected(bool sign)
		{
			if (sign == true)
			{
				cout << "单选按钮\"" << m_caption << "\"被选中" << endl;
			}
			else
			{
				cout << "单选按钮\"" << m_caption << "\"被取消选中" << endl;
			}
		}
	};

	// 编辑框相关类
	class CEditWidget : public CWidget
	{
	public:
		CEditWidget(CMediator* mediator, string caption) : CWidget(mediator, caption) {}

		virtual void Enable(bool sign)
		{
			if (sign == true)
			{
				cout << "编辑框\"" << m_caption << "\"被激活" << endl;
			}
			else
			{
				cout << "编辑框\"" << m_caption << "\"被禁用" << endl;
			}
		}

		// 设置编辑框内容
		void SetText(string text)
		{
			cout << "编辑框\"" << m_caption << "\"内容被设置为:" << text << endl;
		}

		// 查看内容是否为空
		bool isContentEmpty()
		{
			return m_caption.empty();
		}

	private:
		string m_content = "";
	};

	// 具体中介者类
	class CMediatorConcrete : public CMediator
	{
	public:

		~CMediatorConcrete()
		{
			if (m_pButtonLogin) delete m_pButtonLogin;
			if (m_pButtonLogout) delete m_pButtonLogout;
			if (m_pRadioButton1) delete m_pRadioButton1;
			if (m_pRadioButton2) delete m_pRadioButton2;
			if (m_pEditUser) delete m_pEditUser;
			if (m_pEditCode) delete m_pEditCode;
		}

		virtual void createWidget()
		{
			m_pButtonLogin = new CButtonWidget(this, "登录");
			m_pButtonLogout = new CButtonWidget(this, "退出登录");
			m_pRadioButton1 = new CRadioButtonWidget(this, "游客登录");
			m_pRadioButton2 = new CRadioButtonWidget(this, "账号登录");
			m_pEditUser = new CEditWidget(this, "用户名");
			m_pEditCode = new CEditWidget(this, "密码");

			// 设置缺省的控件状态
			m_pButtonLogin->Enable(false);
			m_pButtonLogout->Enable(false);
			m_pRadioButton1->Enable(true);
			m_pRadioButton2->Enable(true);
			m_pEditUser->Enable(false);
			m_pEditCode->Enable(false);
		}

		// 当某个UI控件发生变化时,调用对应的控件状态
		virtual void WidgetChanged(CWidget* theChangedWidget)
		{
			if (theChangedWidget == m_pRadioButton1)
			{
				m_pEditUser->Enable(false);
				m_pEditCode->Enable(false);
				m_pButtonLogin->Enable(false);
				m_pButtonLogout->Enable(false);
				m_pRadioButton1->Selected(true);
				m_pRadioButton2->Selected(false);
			}
			else if (theChangedWidget == m_pRadioButton2)
			{
				m_pEditUser->Enable(true);
				m_pEditCode->Enable(true);
				m_pButtonLogin->Enable(true);
				m_pButtonLogout->Enable(false);
				m_pRadioButton1->Selected(false);
				m_pRadioButton2->Selected(true);
			}
			else if (theChangedWidget == m_pEditUser)
			{
				if (m_pEditUser->isContentEmpty() || m_pEditCode->isContentEmpty())
				{
					m_pButtonLogin->Enable(false);
				}
				else
				{
					m_pButtonLogin->Enable(true);
				}
			}
			else if (theChangedWidget == m_pEditCode)
			{
				if (m_pEditUser->isContentEmpty() || m_pEditCode->isContentEmpty())
				{
					m_pButtonLogin->Enable(false);
				}
				else
				{
					m_pButtonLogin->Enable(true);
				}
			}
			else if (theChangedWidget == m_pButtonLogin)
			{
				m_pButtonLogin->Enable(false);
				m_pButtonLogout->Enable(true);
				m_pRadioButton1->Enable(false);
				m_pRadioButton2->Enable(false);
				m_pEditUser->Enable(false);
				m_pEditCode->Enable(false);
			}
			else if (theChangedWidget == m_pButtonLogout)
			{
				m_pButtonLogin->Enable(true);
				m_pButtonLogout->Enable(false);
				m_pRadioButton1->Enable(true);
				m_pRadioButton2->Enable(true);
				m_pEditUser->Enable(false);
				m_pEditCode->Enable(false);
			}

		}

	public:
		CButtonWidget* m_pButtonLogin;
		CButtonWidget* m_pButtonLogout; // 登录和退出登录

		CRadioButtonWidget* m_pRadioButton1;
		CRadioButtonWidget* m_pRadioButton2; // 游客登录和账号登录

		CEditWidget* m_pEditUser;
		CEditWidget* m_pEditCode; // 用户名和密码

	};

	void test()
	{
		CMediatorConcrete* pMediator = new CMediatorConcrete();
		pMediator->createWidget();

		pMediator->m_pRadioButton1->Changed();
		//pMediator->m_pRadioButton2->Changed();
		//pMediator->m_pEditUser->Changed();
		//pMediator->m_pEditCode->Changed();
		//pMediator->m_pButtonLogin->Changed();
		//pMediator->m_pButtonLogout->Changed();

		delete pMediator;

	}

代码的UML图如下:
在这里插入图片描述
中介者模式的UML图中包含4种角色。
(1) Mediator(抽象中介者类):定义一些接口(抽象方法),后面谈到的各个同事对象(各个UI控件)可以调用这些接口来与中介者进行通信。这里指Mediator 类。
(2) ConcreteMediator(具体中介者类):继承自抽象中介者类,实现父类中定义的抽象方法,其中保存了各个同事对象的指针用于与同事对象进行通信实现协作行为。这里指concrMediator类。
(3) 抽象同事类,CWidget类。
(4) ConcreteColleague(具体同事类),Button,RadioBtn,EditCtrl类。
中介者模式一般用于一组对象以定义良好但复杂的方式进行通信的场合,由中介者负责控制和协调一组对象之间的交互。
优缺点:
a) 集中处理复杂的对象之间的沟通和控制,将以往多对多简化为1对多。
b) 将同事对象进行解耦。
c) 将各种控制逻辑代码都集中(转移)到了中介者类中实现。可能会中介者类的实现变得非常复杂甚至难以维护。要平衡好交互复杂度和实现复杂度。如果中介者实现的太复杂,可能会抵消掉使用该模式在其他方面带来的好处。

中介者模式使用场景

在什么情况下考虑使用中介者模式:
a) 对象之间引用关系过于复杂,某个对象变化会导致大量其他对象的变化。
b) 某个对象与大量其他对象进行连接,通信从而造成该对象很难被复用。
c) 希望将多个类(同事类)中的行为封装到一个类(中介者类)中来实现。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐