65.9K
CodeProject 正在变化。 阅读更多。
Home

中介者设计模式增强为 C++ 组件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.21/5 (9投票s)

2009年4月11日

CPOL

3分钟阅读

viewsIcon

41336

downloadIcon

291

Mediator 设计模式增强版,作为可重用的 C++ 模板类。

引言

本文介绍了一个可重用的 C++ 模板类,它可以很容易地代替 Mediator 设计模式的实现。它带来了以下好处:

  • 它消除了重复实现模式骨架的任务。
  • 即使是不熟悉 Mediator 模式结构的新手开发者,也可以很容易地使用它。
  • 代码重用反过来可以减少错误。
  • 基于社区的事件广播为您的设计提供了更大的灵活性。
  • 您的 Colleague 类不一定需要实现虚函数。

[概念致谢:从模式到组件 - Karine Arnout 的博士论文]

背景

Mediator 设计模式是四人帮 (GoF) 确定的 23 个流行的设计模式之一。传统的观察者模式的实现可以在这里找到。

每次您像上面那样实现 Mediator 模式时,您都必须重复创建 `Mediator`、`Colleague` 和 `ConcreteMediator` 类,而这些类只是设计基础设施所需要的。您的真正业务逻辑将位于 `ConcreteColleague1` 和 `ConcreteColleague1` 类中。

本文中介绍的模板类可以帮助您专注于您的 Concrete Colleague 类,而不是每次都起草设计基础设施。

此外,此模板类还为您提供了一个额外的优势,即**“基于社区的事件订阅”**。 这种技术背后的想法是,在现实世界中,同事们基于一些共同的兴趣在社区中进行交流。 让我们以一个在线 *摄影 * 社区为例; 这个社区有以下特点:

  • 您可以通过访问社区网站并注册来**加入**该社区。
  • 社区将有许多成员,他们都在广播和接收关于摄影的共同**通知**。
  • 您不一定需要认识社区的所有成员。
  • 您可以向社区中的所有其他人发送广播通知,也可以接收其他成员发布的通知。
  • 在成为摄影成员的同时,您也可以加入其他社区,例如“滑雪”社区。
  • 来自摄影和滑雪社区的通知将独立地传递给您。
  • 您可以随时取消订阅社区。

Mediator 模式的传统实现无法提供所有这些灵活性; 而且,它不能作为代码重用。这里介绍的模板类通过使用现成的模板类为您提供所有这些灵活性。

使用代码

以下代码有两个部分。

第 1 部分是 `IColleague`、`Mediator<>` 和 `ColleagueEvent<>` 类的实现。 这三个类共同构成了可重用的 mediator 库头文件(附在本文中)。

第 2 部分是一个示例程序,演示了在典型的 Office 系统模拟器中使用 `ColleagueEvent<>` 类。

#ifndef MEDIATOR_H_
#define MEDIATOR_H_
#include <vector>
#include <algorithm>
namespace GoFPatterns
{
using namespace std;
/*
 * All colleagues are expected to be inherited from this class.
 * Even though it has no functionality now, it is left as a -
 * place holder for future improvements.
 */
class IColleague
{
protected:
    IColleague()
    {
    }
};
/*
 * Forward declaration of ColleagueEvent class
 */
template<typename EventArgType>
class ColleagueEvent;
/*
 * Mediator class is a singleton class and its only one instance of it will -
 * be created for each type of colleague collaboration.
 * An Instance of this class hold the reference to all ColleagueEvent objects -
 * which fire/ receive same Event Argument Type. Since this class is not -
 * meant to used public, it is exposed only to ColleagueEvent class as -
 * a friend.
 */
template<typename EventArgType>
class Mediator
{
    typedef vector<ColleagueEvent<EventArgType>*> EventList;
private:
    //List of ColleagueEvents in this community.
    EventList colleagues;

    static Mediator<EventArgType>* instance; //Singleton implementation
    /*
     * Access to the only one possible type specific object of this class.
     */
    static Mediator<EventArgType>& GetInstance()
    {
        if (!instance)
        {
            instance = new Mediator();
        }
        return *instance;
    }
    /*
     * Register a ColleagueEvent in the list of colleagues in this community.
     */
    void RegisterCollegue(ColleagueEvent<EventArgType> *colEvent)
    {
        colleagues.push_back(colEvent);
    }
    /*
     * When a ColleagueEvent is fired, notify all other Events in -
     * this community
     */
    void FireEvent(ColleagueEvent<EventArgType> *source, EventArgType eventArg)
    {
        for (unsigned int i = 0; i < colleagues.size(); i++)
        {
            //Notify all Events other than the one who fired the event.
            if (colleagues[i] != source)
            {
                colleagues[i]->handlerProc(source->eventContext, eventArg,
                        colleagues[i]->eventContext);
            }
        }
    }
    /*
     * Remove a ColleagueEvent from the list of colleagues in this community.
     */
    void UnregisterCollegue(ColleagueEvent<EventArgType> *colEvent)
    {
        typename EventList::iterator itr = find(colleagues.begin(),
                colleagues.end(), colEvent);
        if (itr != colleagues.end())
        {
            colleagues.erase(itr);
        }
    }
    friend class ColleagueEvent<EventArgType> ;
};

/*
 * The CollegueEvent template class is instantiated by means of -
 * an EventArgumentType.When an object is created, it registers itself with -
 * Mediator<> matching its EventArgAtype. Thus it becomes a member of the -
 * community of ColleagueEvents with same EventArgType.
 * When a ColleagueEvent object is fired, it is informed to the community -
 * Mediator<> and Mediator broadcast the event to all other CollegueEvents -
 * in that community.
 */
template<typename EventArgType>
class ColleagueEvent
{
    typedef void (*ColleagueEventHandler)(IColleague *source,
            EventArgType eventArg, IColleague* context);
    IColleague * eventContext; //Context colleague who fires the event
    ColleagueEventHandler handlerProc; //Event handler function pointer
public:
    /*
     * Constructor receives the event source context and the EventHandler
     * function pointer. Also register this object with the Mediator<> -
     * to join the community.
     */
    ColleagueEvent(IColleague *source, ColleagueEventHandler eventProc) :
        eventContext(source), handlerProc(eventProc)
    {
        //Register with mediator
        Mediator<EventArgType>::GetInstance().RegisterCollegue(this);
    }
    /*
     * Destructor - unregister the object from community.
     */
    virtual ~ColleagueEvent()
    {
        Mediator<EventArgType>::GetInstance().UnregisterCollegue(this);
    }
    /*
     * FireEvent - Inform the Mediator<> that the event object is triggered.
     * The Mediator<> then will broadcast this to other ColleagueEvents -
     * in this community.
     */
    void FireEvent(EventArgType eventArg)
    {
        Mediator<EventArgType>::GetInstance().FireEvent(this, eventArg);
    }
    friend class Mediator<EventArgType> ;
};
//Define the static member of Mediator<> class
template<typename EventArgType>
Mediator<EventArgType>* Mediator<EventArgType>::instance = 0;

} //End name space GoFPatterns
#endif /* MEDIATOR_H_ */

示例用法

//==========EXAMPLE USAGE OF MEDIATOR PATTERN LIBRARY =======================

#include <iostream>
#include "../GOFLib/Mediator.h"
/*
 * This example illustrates an office scenario.
 * In this office, there are different kind of Employee s as
 * Salesmen, Managers, System Administrators, Financial Managers and CEO
 * Two communities are identified like general Staff and Administration staff
 * SalesMen, Managers, SysAdmins & Finance Managers
 * are joined in general staff community.
 * Managers, Finance Managers and CEO belong to Administration community.
 * These two communities, General Staff & Administration Staff are defined by two
 * Event Argument types such as StaffMsg (for General Staff )
 * and AdminMsg (for Administration Staff).
 * For ease of event handling, a class named GeneralStaff is there from which the
 * SalesMen, Managers, SysAdmins and FinanceMangers are inherited from.
 * Employee is the root class which is inherited from
 * IColleague which is a must for all Colleagues.
 */
namespace GoFExample
{
using namespace std;
/*
 * Staff message will be used as an EventArgument and
 * All General Staff are expected to subscribe / publish this kind of Events
 */
class StaffMsg
{
public:
    string msgName;
    string msgData;
    StaffMsg(string eName, string eData) :
        msgName(eName), msgData(eData)
    {
    }
};

/*
 * Admin message will be used as an EventArgument and
 * All Administration Staff are expected to subscribe / publish this kind of Events
 */
class AdminMsg
{
public:
    string eventName;
    string eventTime;
    AdminMsg(string eName, string eTime) :
        eventName(eName), eventTime(eTime)
    {
    }
};
/*
 * Base class for all employees in the company
 * It is extended from abstract class IColleague to become
 * eligible to subscribe colleague events
 */
class Employee: public GoFPatterns::IColleague
{
public:
    string title; //Designation of employee
    string name; // Employee name
    /*
     * Constructor to set Designation and name
     */
    Employee(string eTitle, string eName) :
        title(eTitle) // Set designation & name
                , name(eName)
    {
    }
    virtual ~Employee()
    {
    }

};
/*
 * General staff class handles the common events to which most -
 * of the employees are interested in.
 */
class GeneralStaff: public Employee
{
protected:
    //Declare a colleague event which represent General Staff events.
    GoFPatterns::ColleagueEvent<StaffMsg> generalStaffEvent;
    //Join the General Staff community
public:
    /*
     * Constructor for Initializing GeneralStaffEvent
     */
    GeneralStaff(string eTitle, string eName) :
        Employee(eTitle, eName) //Initialize title and name
                , generalStaffEvent(this, OnColleagueEvent)
                //Initialize GeneralStaff Colleague Event
    {
    }
    /*
     * Display details of the received event, its data,
     * its source and recipient context.
     */
    static void OnColleagueEvent(IColleague *source, StaffMsg data,
            IColleague* context)
    {
        Employee *srcCollegue = static_cast<Employee*> (source);
        Employee *ctxCollegue = static_cast<Employee*> (context);
        cout << endl << ctxCollegue->title 
                << " - " << ctxCollegue->name
                << " is notified by " 
                << srcCollegue->title << " - "
                << srcCollegue->name 
                << " of STAFF Event " << data.msgName
                << " with " << data.msgData;
    }
};

/*
 * Sales men is a general staff who receives all general staff notification
 * Now he does not raise any General Staff event.
 */
class SalesMen: public GeneralStaff
{
public:
    SalesMen(string eName) :
        GeneralStaff("Sales Man", eName)
    {
    }
};

/*
 * Manager is a General staff by primary design.
 * Also he has an additional subscription to the Administration community.
 * His general staff events are handled from the base class itself.
 * But the Administration events are handled in this class itself to show -
 * better fine tuned response.
 */
class Manager: public GeneralStaff
{
    //Join the administration community
    GoFPatterns::ColleagueEvent<AdminMsg> adminEvent;
public:
    Manager(string eName) :
        GeneralStaff("Manager", eName),
        adminEvent(this, OnAdminEvent)
        // Initialize adminEvent with Event Handler function name
    {
    }
    /*
     * Book a meeting room and notify all other General staff that
     * the meeting room is reserved by this Manager.
     */
    void BookMeetingRoom(string meetingRoomName)
    {
        //Fire a GeneralStaff Event that the meeting room is booked.
        generalStaffEvent.FireEvent(StaffMsg("Meeting Room Booking",
                meetingRoomName));
    }
    /*
     * Handle an administration event notification..
     * Now it just prints the event details to screen.
     */
    static void OnAdminEvent(IColleague *source, AdminMsg data,
            IColleague* context)
    {
        Employee *srcCollegue = static_cast<Employee*> (source);
        Employee *ctxCollegue = static_cast<Employee*> (context);
        cout << endl << "Manager - " 
                << ctxCollegue->name << " is notified by "
                << srcCollegue->title 
                << " - " << srcCollegue->name
                << " of Admin Event " 
                << data.eventName << " @ "
                << data.eventTime;
    }
};

/*
 * SysAdmin is a General staff. He receives all GenaralStaff events
 * Further more, he notifies all General staff when there is a
 * Software updation is required.
 */
class SysAdmin: public GeneralStaff
{
public:
    SysAdmin(string eName) :
        GeneralStaff("Sys Admin", eName)
    {
    }
    /*
     * Notify all General staff that the Software of each staff has to be updated.
     */
    void AdviceForSoftwareUpdate(string swName)
    {
        //Fire a GeneralStaff Event that the Software of each staff has to be updated.
        generalStaffEvent.FireEvent(StaffMsg("Software Update Advice", swName));
    }
};

/*
 * Finance Manager is a General staff by primary design.
 * Also he has an additional subscription to the Administration community.
 * His general staff events are handled from the base class itself.
 * But the Administration events are handled in this class itself to show -
 * better fine tuned response.
 */
class FinanceManager: public GeneralStaff
{
    //Join the administration community
    GoFPatterns::ColleagueEvent<AdminMsg> adminEvent;
public:
    FinanceManager(string eName) :
        GeneralStaff("Finance Manager", eName), adminEvent(this, OnAdminEvent)
    {
    }
    /*
     * Finance manager can raise an event to all General staff
     * to request for the Income tax document.
     */
    void RequestForIncomeTaxDocument(string docName)
    {
        generalStaffEvent.FireEvent(StaffMsg("IT Doc Request", docName));
    }
    /*
     * Handle an administration event notification..
     * Now it just prints the event details to screen.
     */
    static void OnAdminEvent(IColleague *source, AdminMsg data,
            IColleague* context)
    {
        Employee *srcCollegue = static_cast<Employee*> (source);
        Employee *ctxCollegue = static_cast<Employee*> (context);
        cout << endl << "Finance Manager - " 
                << ctxCollegue->name
                << " is notified by " 
                << srcCollegue->title << " - "
                << srcCollegue->name 
                << " of Admin Event " << data.eventName
                << " @ " << data.eventTime;
    }
};

/*
 * CEO - is not a General staff so he does not receive the
 * General staff events like, Meeting room Request ,  -
 * Software Update request, IncomeTaxDocumentRequest etc
 * CEO Has joined in the Administration Community where -
 * Managers and Finance Manager are members of.
 * Also CEO can raise an Administration Event to hold a -
 * Marketing strategy discussion.
 */
class CEO: public Employee
{
    //Join the administration community
    GoFPatterns::ColleagueEvent<AdminMsg> adminEvent;

public:
    CEO(string eName) :
        Employee("CEO", eName), adminEvent(this, OnAdminEvent)
    {
    }

    /*
     * Handle an administration event notification..
     * Now it just prints the event details to screen.
     */
    static void OnAdminEvent(IColleague *source, AdminMsg data,
            IColleague* context)
    {
        Employee *srcCollegue = static_cast<Employee*> (source);
        Employee *ctxCollegue = static_cast<Employee*> (context);
        cout << endl << "CEO- " 
                << ctxCollegue->name << " is notified by "
                << srcCollegue->title 
                << " - " << srcCollegue->name
                << " of Admin Event " 
                << data.eventName << " @ "
                << data.eventTime;
    }
    /*
     * Raise an Admin. Event to hold a Marketing Strategy discussion.
     */
    void HoldAdministrationMeeting(string agenda, string time)
    {
        // Fire and Admin Event to notify all other Administration community members.
        adminEvent.FireEvent(AdminMsg(agenda, time));
    }
};

/*
 * Program entry point; main() function
 */
int main()
{
    Manager mng1("Vivek"), mng2("Pradeep");
    SysAdmin sys1("Sony");
    SalesMen sl1("Biju"), sl2("Santhosh"), sl3("David");
    CEO ceo1("Ramesh");
    mng1.BookMeetingRoom("Zenith Hall");
    cout << endl
            << "===========================================================";
    FinanceManager fin1("Mahesh");
    sys1.AdviceForSoftwareUpdate("Win XP SP3");
    cout << endl
            << "===========================================================";
    fin1.RequestForIncomeTaxDocument("Form 12C");
    cout << endl
            << "===========================================================";
    ceo1.HoldAdministrationMeeting("European Marketing Plan",
            "Wednesday 4:00PM");
    cout << endl
            << "===========================================================";

    return 0;
}
} //End name space GoFExample

程序输出

关注点

我试图像 Karine 的论文文档中显示的 Eiffel 示例一样实现上述模式。

完成之后,我感觉非常不愉快,因为它看起来太不完美了。所以我删除了所有代码,然后仅仅依靠我的想象力从头开始,从而形成了社区事件的概念。我认为遵循你的想象力比预先写好的文档更好,才能找到新的想法。

历史

  • 2009 年 4 月 11 日 - 初始版本。
© . All rights reserved.