メディエーターパターン

はじめに:

【概要】 メディエーターパターン(Mediator Pattern)は、調停者パターンとも呼ばれ、行動型デザインパターンの一つです。このパターンでは、複数のオブジェクト間の相互作用をカプセル化するための中間オブジェクト(メディエーター)を使用し、各オブジェクトが直接的に相互参照しなくなるようにします。これにより、オブジェクト間の結合度が低減され、システムの保守性と拡張性が向上します。 複数のオブジェクトがあり、それらの間で複雑な依存関係がある場合、その構造は以下のようになることがあります。

このような依存関係は理解が難しくなります。そこで、中間オブジェクトであるメディエーターを導入して、相互作用を調整することが可能です。メディエーターパターンにより、システム全体のネットワーク構造がメディエーターを中心にしたスター構造へと変換されます。各具体的なオブジェクトは、他のオブジェクトと直接的な連携を取るのではなく、メディエーターを通じてやり取りを行うことになります。

【基本構成】 メディエーターパターンには以下の要素が含まれます:

抽象メディエーター(Mediator):各具象コラボレータ間の通信インターフェースを定義します。 具象メディエーター(Concrete Mediator):抽象メディエーターを実装し、各具象コラボレータ間の関係を調整します。すべてのコラボレータを把握し、コラボレータからのメッセージを受け取り、コラボレータに対して指示を出す必要があります。 抽象コラボレータ(Colleague):コラボレータのインターフェースを定義し、メディエーターへの参照を保持します。 具象コラボレータ(Concrete Colleague):抽象コラボレータを実装し、それぞれのコラボレータは自分の振る舞いのみを知り、他のコラボレータの詳細は知りません。すべてのコラボレータはメディエーターと通信し、メディエーターによって他のコラボレータとのやりとりを調整します。

#include <iostream>
#include <string>
#include <vector>
#include <map>
 
using namespace std;
 
 
// クラス定義
 
// 抽象メディエータークラス
class ChatRoomMediator
{
// メンバーアクセッサ
public:
    // 全ユーザーの取得
    virtual std::map<string, ChatUser*> GetUsers() = 0;
    // ユーザーの追加
    virtual void AddUser(ChatUser *user) = 0;
    // メディエーターによる全員へのメッセージ送信
    virtual void SendMessage(string sender, string message) = 0;
    
};
 
// 具象メディエータークラス 
class ChatRoomMediatorImpl : public ChatRoomMediator
{
// メンバーデータ
private:
    std::map <string, ChatUser*> _chatUsers;//<ユーザー名, ユーザーインスタンス>

	// メンバーアクセッサ
public:
    // 全ユーザーの取得(オーバーライド)
    std::map<string, ChatUser*> GetUsers() override
	{
        return _chatUsers;
    }
    // ユーザーの追加(オーバーライド)
    void AddUser(ChatUser *user) override
	{
        _chatUsers.insert(std::pair<string, ChatUser*>(user->GetName(), user));
    }
    // メディエーターによる全員へのメッセージ送信(オーバーライド)
    void SendMessage(string sender, string message) override
	{
        for(map<string, ChatUser*>::iterator it = _chatUsers.begin(); it != _chatUsers.end(); it++)
		{
            if(it->first != sender)// 送信者以外の全員に送信
			{
                it->second->ReceiveMessage(sender, message);
            }
        }
    }
};

// 抽象ユーザークラス -- インターフェース
class ChatUser
{
// メンバーアクセッサ
public:    
    // 現在のユーザー名の取得
    virtual string GetName() = 0;    
    // 他の人にメッセージを送信(メディエーターを介して)
    virtual void SendMessage(string message) = 0; 
    // メッセージ受信関数インターフェース
    virtual void ReceiveMessage(string sender, string message) = 0;   
    // 全メッセージの取得
    virtual std::vector<string> GetAllMessages() = 0;    
    
protected:
	// 受信したメッセージをリストに追加
    virtual void AddRecvMessageIntoVec(string message) = 0;  
};
 
// 具象ユーザークラス 
class ConcreteChatUser : public ChatUser
{
// メンバーデータ
private:   
    string _userName;//ユーザー名 
    ChatRoomMediator * _mediator;//メディエーター
    std::vector<string> _recvMessages;//受信メッセージリスト
	
// メンバーアクセッサ
public:
    ConcreteChatUser(string name, ChatRoomMediator *mediator) //コンストラクター
	{ 
        this->_userName = name;
        this->_mediator = mediator;
        this->_mediator->AddUser(this);
    }
    // 現在のユーザー名の取得
    string GetName() override 
	{
        return _userName;
    }
    // 他の人にメッセージを送信(メディエーターを介して)
    void SendMessage(string message) override 
	{
        // メディエーター経由でメッセージを送信
        _mediator->SendMessage(_userName, message);//(送信者名, メッセージ)
    } 
    // メッセージ受信関数インターフェース
    void ReceiveMessage(string sender, string message) override
	{
        string messages = _userName + " received: " + message;
        std::cout << messages << endl;
    }
    // 全メッセージの取得
    std::vector<string> GetAllMessages() override 
	{
        return _recvMessages;
    }
      
protected:
	// 受信したメッセージをリストに追加
    void AddRecvMessageIntoVec(string message) override
	{
        _recvMessages.push_back(message);
    }
};
 
 
int main()
{
    // ユーザー数
    int userNum = 0;
    std::cin >> userNum;
    // 全ユーザーを保存
    std::vector<string> usersV;
    usersV.resize(userNum);  
    for(int i = 0; i < userNum; i++)
	{
        std::cin >> usersV[i];
    }
    
    // 抽象メディエーター
    ChatRoomMediator *mediator = new ChatRoomMediatorImpl();// 具象メディエーターのインスタンス生成
    // 抽象ユーザークラス
    ChatUser *user = nullptr;
    // メディエーターに全ユーザーを登録
    for(int i = 0; i < userNum; i++)
	{
        // 具象ユーザークラスの生成
        user = new ConcreteChatUser(usersV[i], mediator);
    }
    // これでメディエーターに全ユーザーが登録完了
    
    // メッセージ送信処理
    for(int i = 0; i < userNum; i++)
	{
        // 送信者とメッセージを取得
        string senderName = "";
        string sendMessageInfo = "";
        // 入力
        std:: cin >> senderName >> sendMessageInfo;
        // ユーザー(送信者)がメディエーター経由でメッセージ送信
        mediator->SendMessage(senderName, sendMessageInfo);
    }
    
    // デストラクタ
    if(user != nullptr)
	{
        delete user;
        user = nullptr;
    }
    delete mediator;
    mediator = nullptr;
    return 0; 
}

【補足:プロキシーパターンとの違い】 メディエーターパターン(Mediator Pattern)とプロキシーパターン(Proxy Pattern)は似ているように見えるかもしれませんが、これらは異なるデザインパターンです。メディエーターパターンは、システム内の各オブジェクト間の直接的な結合を減らし、中間オブジェクトであるメディエーターを導入することで、オブジェクト間の通信をメディエーターに集中させることを目的としています。一方、プロキシーパターンでは、クライアントがプロキシーを通して対象オブジェクトと通信します。プロキシーは、対象オブジェクトのメソッド呼び出し前後で追加の操作を行うことができ、その主な目的はオブジェクトへのアクセスを制御することです。これらのパターンは、異なる問題に対応しています。

原文リンク:https://blog.csdn.net/K1_uestc/article/details/135680591

第二章:

1 概念 一連のオブジェクト間の相互作用を仲介者オブジェクトでカプセル化し、各オブジェクトが明示的に相互参照することなく通信できるようにします。これにより、結合度が緩くなり、オブジェクト間の相互作用を独立して変更できます。

2 パターン構造

メディエーターは二つの役割を担います: 中継機能(構造的):メディエーターが提供する中継機能により、各コラボレータは他のコラボレータを明示的に参照する必要がなくなります。他のコラボレータと通信する際はメディエーターを介して行います。これは、メディエーターの構造的サポートです。 調整機能(動作的):メディエーターはさらに、コラボレータ間の関係をカプセル化できます。コラボレータはすべてメディエーターと通信するだけで、メディエーターが具体的に何をするかを知らなくても構いません。メディエーターは内部に封印された調整ロジックに基づき、コラボレータからのリクエストを処理し、コラボレータ間の関係を分離・カプセル化します。これは、メディエーターの動作的サポートです。

Mediator: 抽象メディエーターで、各コラボレータ間の通信インターフェースを定義します。 ConcreteMediator: 抽象メディエーターのサブクラスで、各コラボレータ間の協調動作を実現します。各コラボレータへの参照を把握し、維持します。 Colleague: 抽象コラボレータで、各コラボレータの共通メソッドを定義します。 ConcreteColleague: 各コラボレータはメディエーターの参照を保持します。他のコラボレータと通信する際はまずメディエーターとやり取りし、メディエーターを通じて他のコラボレータと通信します。

3 コード実装 例:仮想チャットルーム あるフォーラムシステムに仮想チャットルームを追加し、会員が情報を交換できるようにします。一般会員(CommonMember)は他の会員にテキストメッセージを送信できます。ダイヤモンド会員(DiamondMember)はテキストメッセージだけでなく画像も送信できます。このチャットルームは不適切な文字(例:「日」など)をフィルタリングし、画像サイズも制限できます。メディエーターパターンを使ってこの仮想チャットルームを設計します。

#include<iostream>
#include <string>
#include <map>

using namespace std;

class Member;
// 抽象メディエータークラスAbstractChatroom(抽象チャットルーム)
class AbstractChatroom {
public:
	virtual void registerMember(Member* member) = 0;
	virtual void sendText(string from, string to, string message) = 0;
	virtual void sendImage(string from, string to, string image) = 0;
};
// 抽象コラボレータクラスMember(抽象会員)
class Member
{

protected:
	AbstractChatroom *chatroom;
	string name;
public:
	Member(string name) {
		this->name = name;
	}

	string getName() {
		return this->name;
	}

	void setName(string name) {
		this->name = name;
	}

	AbstractChatroom* getChatroom() {
		return chatroom;
	}

	void setChatroom(AbstractChatroom* chatroom) {
		this->chatroom = chatroom;
	}

	virtual void sendText(string to, string message) = 0;
	virtual void sendImage(string to, string image) = 0;

	void receiveText(string from, string message) {
		cout << from << "が" << this->name << "にテキストを送信しました。内容:" << message << endl;
	}

	void receiveImage(string from, string image) {
		cout << from << "が" << this->name << "に画像を送信しました。内容:" << image << endl;
	}

};
// 具象メディエータークラスChatgroup(具象チャットルーム)
class ChatGroup : public AbstractChatroom {
public:
	void registerMember(Member* member) {
		if (!members.count(member->getName())) {
			members.insert(make_pair(member->getName(), member));
			member->setChatroom(this);
		}
	}

	void sendText(string from, string to, string message) {
		Member *member = members[to];
		member->receiveText(from, message);
	}

	void sendImage(string from, string to, string image) {
		Member *member = members[to];
		if (image.length() > 5) {
			cout << "画像が大きすぎます。送信できませんでした!" << endl;
		}
		else {
			member->receiveImage(from, image);
		}

	}
private:
	map<string, Member*> members;
};

// 具象コラボレータクラスCommonMember(一般会員)
class CommonMember : public Member {
public:
	CommonMember(string name) : Member(name) {

	}

	void sendText(string to, string message) {
		cout << "一般会員がメッセージを送信しました:" << endl;
		chatroom->sendText(name, to, message);
	}

	void sendImage(string to, string image) {
		cout << "一般会員は画像を送信できません!" << endl;
	}
};
// 具象コラボレータクラスDiamondMember(ダイヤモンド会員)
class DiamondMember : public Member {
public:
	DiamondMember(string name) : Member(name) {

	}

	void sendText(string to, string message) {
		cout << "ダイヤモンド会員がメッセージを送信しました:" << endl;
		chatroom->sendText(name, to, message);
	}

	void sendImage(string to, string image) {
		cout << "ダイヤモンド会員が画像を送信しました:" << endl;
		chatroom->sendImage(name, to, image);
	}
};

int main(int argc, char* argv[])
{

	// チャットルームを作成
	ChatGroup happyChat;

	// メンバーを作成
	CommonMember member1("張三");
	DiamondMember member2("李四");

	// 登録
	happyChat.registerMember(&member1);
	happyChat.registerMember(&member2);

	// メッセージ送信
	member1.sendText("李四", "李四、こんにちは!");
	member2.sendText("張三", "張三、こんにちは!");
	member1.sendText("李四", "今日は天気がとても良いですね");
	member1.sendImage("李四", "良い天気");
	member2.sendImage("張三", "大きな太陽");
	member2.sendImage("張三", "太陽");
}

4 利点と欠点 4.1 利点 メディエーターを導入することで、システムのネットワーク構造がメディエーター中心のスター構造へと変わり、メディエーターが中継と調整の役割を担います。 オブジェクト間の相互作用を簡略化し、各コラボレータの結合を緩和し、サブクラスの生成を削減できます。複雑なオブジェクト間の相互作用については、メディエーターの導入により、各コラボレータの設計と実装が簡素化されます。

4.2 欠点 具体的なメディエータークラス内にコラボレータ間の相互作用の詳細が含まれるため、メディエータークラスが非常に複雑になる可能性があり、システムの保守が困難になることがあります。

5 適用場面 オブジェクト間の参照関係が複雑で、依存関係が混乱しており理解しづらい場合;あるオブジェクトが多くのオブジェクトを参照し、それらと直接通信しているため、再利用が困難な場合;複数のクラスの動作をカプセル化する中間クラスが必要だが、多くのサブクラスを生成したくない場合。

原文リンク:https://blog.csdn.net/cutemypig/article/details/110039394

タグ: デザインパターン メディエーターパターン C++ オブジェクト指向プログラミング 調停者パターン

5月26日 22:39 投稿