中介者模式,說一說貝殼找房

 離開學校參加工作之前,你一定是有一段時間是在找租,Jungle也是如此。

Jungle爲了找到合適的房子,沿着地鐵線一個小區一個小區的去問門衛問保安,或者照着小區門口展板上的房東的聯繫方式去找房東……此事已經過去大半年了,但Jungle現在想來還是覺得很麻煩!麻煩在哪裏?得親自走親自聯繫各個房東,通信錄和微信得加好多房東……

其實有更省事的辦法,那就是找中介,租房中介哪兒都是。雖然貴(主要原因),但是的確爲租客省了很多事,其實也爲房東省了很多事。

1.中介者模式簡介

上述Jungle租房的例子如上圖,如果Jungle自己去租房,得和各個房東親自交互,如果另一個租客賤萌兔也在自己找房,同樣也得和很多房東打交道。房東也是一樣,得和衆多不同的租客聯繫。如果有中介者了,房東們只需要去中介者那裏註冊一下,自己的房子在哪兒、什麼戶型設施、價格多少,就ok了;Jungle和賤萌兔也只需要和一個人打交道,那就是中介。中介的出現使兩邊都省去了不少事

軟件設計模式中,也有一種類似的解決方案,那就是中介者模式——

中介者模式:

定義一個對象來封裝一系列對象的交互。中介者模式使各個對象之間不需要顯示地相互引用,從而使其耦合鬆散,而且用戶可以獨立地改變它們之間的交互。

如果一個系統裏各個對象之間存在多對多的相互關係,可以將對象之間的一些交互行爲從各個對象中分離出來,集中封裝在一箇中介者對象中,使其耦合鬆散,並由中介者統一協調。通過中介者,對象之間的多對多關係就簡化了相對更簡單的一對多關係。 

2.中介者模式結構

中介者模式的UML圖如下,爲了便於擴展,系統引入了抽象中介者。

由圖可知,中介者模式主要有以下角色:

  • Mediator(抽象中介者):聲明一個用於與各個同事對象之間交互的接口,通常聲明一個註冊方法,用於增加同事對象;
  • ConcreteMediator(具體中介者):實現上面的接口,協調各個同事對象來實現協作行爲,維持對各個同事對象的引用;
  • Colleague(抽象同事類):聲明各個同事類公有的接口,同時維持了一個對抽象中介者類的引用
  • ConcreteColleague(具體同事類) 具體實現接口,具體同事類只需與中介者通信,通過中介者完成與其他同事類的通信。

中介者模式的核心在於引入了中介者類,中介者類承擔了兩個層次的職責:

  • 結構上起中轉作用:通過中介者的中轉,各個同事之間不必再相互顯示調用或引用,只需通過中介者實現間接調用的目的;
  • 行爲上起協調作用:中介者可以進一步地將同事之間的關係進行封裝,同事可以一致地和中介者進行交互,而不必指出中介者具體該如何操作,中介者根據封裝在自身內部的協調邏輯對同事的請求進一步處理,將同事成員之間的關係行爲進行分離和封裝。

3.中介者模式代碼實例

本節Jungle將採用中介者模式模擬“租客——房租中介——房東”之間的愛恨情仇!

(本例的代碼相對較複雜,具體代碼資源請見https://github.com/FengJungle/DesignPattern

Jungle和賤萌兔想要通過房屋中介(Agency)租房,需要去中介處瞭解房東(Landlord)的信息(姓名,價格,地址和聯繫方式);房東們(Landlord)需要在中介處註冊自己的房源,同時也可以從中介處瞭解租客(Tenant)的信息(姓名)。

本例的UML圖如下: 

3.0.公共頭文件

爲區分房東和租客,Jungle定義了一個枚舉類型和對應的setter、getter方法:

#ifndef __COMMON_H__
#define __COMMON_H__

// 公共頭文件

#include <vector>
using namespace std;

enum PERSON_TYPE
{
	NONE_PERSON,
	LANDLORD,
	TENANT
};

#endif  //__COMMON_H__

3.1.中介者

3.1.1.抽象中介者

// 抽象中介者
class Mediator
{
public:
	Mediator(){}
	// 聲明抽象方法
	virtual void operation(Colleague*) = 0;
	// 聲明註冊方法
	virtual void registerMethod(Colleague*) = 0;
};

3.1.2.具體中介者Agency 

具體中介者就是真實的中介對象類,他手裏有房東的名單(landlordList)和租客名單(tenantList),房東和租客通過registerMethod()在中介處登記註冊。同時,房東可以詢問中介租客信息,租客也可以向中介詢問房東信息。

// 具體中介者
class Agency:public Mediator
{
public:
	Agency(){}
	void registerMethod(Colleague* person){
		switch (person->getPersonType()){
		case LANDLORD:
			landlordList.push_back((Landlord*)person);
			break;
		case TENANT:
			tenantList.push_back((Tenant*)person);
			break;
		default:
			printf("wrong person\n");
		}
	}
	void operation(Colleague* person){
		switch (person->getPersonType()){
		case LANDLORD:
			for (int i = 0; i < tenantList.size(); i++){
				tenantList[i]->answer();
			}
			break;
		case TENANT:
			for (int i = 0; i < landlordList.size(); i++){
				landlordList[i]->answer();
			}
			break;
		default:
			break;
		}
	}
private:
	vector<Landlord*>landlordList;
	vector<Tenant*>tenantList;
};

3.2.同事類

3.2.1.抽象同事類

// 前向聲明
class Mediator;
class Agency;

// 抽象同事類
class Colleague
{
public:
	Colleague(){}
	void setMediator(Mediator* iMediator){
		this->mediator = iMediator;
	}
	Mediator* getMediator(){
		return this->mediator;
	}
	void setPersonType(PERSON_TYPE iPersonType){
		this->personType = iPersonType;
	}
	PERSON_TYPE getPersonType(){
		return this->personType;
	}
	virtual void ask() = 0;
	virtual void answer() = 0;
private:
	PERSON_TYPE personType;
	Mediator* mediator;
};

3.2.2.具體同事類——房東(Landlord)

聲明:

// 具體同事類:房東
class Landlord :public Colleague
{
public:
	Landlord();
	Landlord(string iName, int iPrice, string iAddress, string iPhoneNum);
	void ask();
	void answer();
private:
	string name;
	int price;
	string address;
	string phoneNumber;
};

實現:

#include "Colleague.h"
#include "Mediator.h"

Landlord::Landlord(){
	name = "none";
	price = 0;
	address = "none";
	phoneNumber = "none";
	setPersonType(NONE_PERSON);
}

Landlord::Landlord(string iName, int iPrice, 
	string iAddress, string iPhoneNum){
	name = iName;
	price = iPrice;
	address = iAddress;
	phoneNumber = iPhoneNum;
	setPersonType(LANDLORD);
}

void Landlord::answer(){
	printf("房東姓名:%s, 房租:%d, 地址:%s, 聯繫電話:%s\n",
		name.c_str(), price, address.c_str(), phoneNumber.c_str());
}

void Landlord::ask(){
	printf("房東%s查看租客信息:\n",name.c_str());
	(this->getMediator())->operation(this);
}

 3.2.3.具體同事類——租客(Tenant) 

聲明:

// 具體同事類:租客
class Tenant :public Colleague
{
public:
	Tenant();
	Tenant(string name);
	void ask();
	void answer();
private:
	string name;
};

實現:

#include "Colleague.h"
#include "Mediator.h"

Tenant::Tenant(){
	name = "none";
	setPersonType(NONE_PERSON);
}

Tenant::Tenant(string iName){
	name = iName;
	setPersonType(TENANT);
}

void Tenant::ask(){
	printf("租客%s詢問房東信息\n", name.c_str()); 
	(this->getMediator())->operation(this);
}

void Tenant::answer(){
	printf("租客姓名:%s\n", name.c_str());
}

3.3.客戶端代碼示例及效果

#include <iostream>
#include "Mediator.h"
#include "Colleague.h"

int main()
{
	// 創建租房中介
	Agency *mediator = new Agency();

	// 創建3位房東
	Landlord *fangdong1 = new Landlord("劉備", 1350, "成都市雙流區", "1351025");
	Landlord *fangdong2 = new Landlord("關羽", 1500, "成都市武侯區", "1378390");
	Landlord *fangdong3 = new Landlord("張飛", 1000, "成都市龍泉驛", "1881166");
	fangdong1->setMediator(mediator);
	fangdong2->setMediator(mediator);
	fangdong3->setMediator(mediator);
	// 房東在中介處登記註冊房源信息
	mediator->registerMethod(fangdong1);
	mediator->registerMethod(fangdong2);
	mediator->registerMethod(fangdong3);

	// 創建兩位租客Jungle和賤萌兔
	Tenant *jungle = new Tenant("Jungle");
	Tenant *jianmengtu = new Tenant("賤萌兔");
	jungle->setMediator(mediator);
	jianmengtu->setMediator(mediator);
	// Jungle和賤萌兔在中介處登記求租信息
	mediator->registerMethod(jungle);
	mediator->registerMethod(jianmengtu);

	jungle->ask();
	printf("\n\n");
	fangdong1->ask();

	printf("\n\n");
	system("pause");
	return 0;
}

運行結果如下:

4.總結

優點:

  • 簡化了對象之間的交互,通過中介者,對象之間的多對多關係就簡化了相對更簡單的一對多關係;
  • 可將各個同事對象解耦,利於各個同事之間的鬆散耦合,可獨立地改變和複用每一個同事對象,增加新的中介者和同事都比較方便,符合開閉原則;
  • 可減少子類生成,將原本分佈於多個對象之間的行爲封裝在一起,只需生成新的具體中介者類就可以改變這些行爲。 

缺點:

  • 具體中介者類中包含了大量與同事之間交互的細節和邏輯,可能使得中介者類很複雜以至於難以管理維護。

適用環境:

  • 系統中的對象之間存在複雜的交互關係,使得系統內邏輯錯綜複雜,難以管理;
  • 一個對象引用了其他很多對象,並直接和這些對象交互,導致該對象難以複用。

(本例的代碼相對較複雜,具體代碼資源請見https://github.com/FengJungle/DesignPattern


歡迎關注知乎專欄:Jungle是一個用Qt的工業Robot

歡迎關注Jungle的微信公衆號:Jungle筆記

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章