c/c++设计模式---观察者模式

namespace _nmsp1
{
    class Fighter; //类前向声明
    list<Fighter*> g_playerList; 

    //玩家父类(以往的战斗者类)
    class Fighter
    {
    public: 
        Fighter(int tmpID, string tmpName) :m_iPlayerID(tmpID), m_sPlayerName(tmpName) //构造函数
        {
            m_iFamilyID = -1; //-1表示没有加入任何家族
        }
        virtual ~Fighter() {} //析构函数

    public:
        void SetFamilyID(int tmpID) //加入家族的时候要设置家族ID
        {
            m_iFamilyID = tmpID;
        }

    public:
        void SayWords(string tmpContent) //玩家说了某句话
        {
            if (m_iFamilyID != -1)
            {
                //该玩家属于某个家族,应该把聊天内容信息传送给该家族的其他玩家
                for (auto iter = g_playerList.begin(); iter != g_playerList.end(); ++iter)
                {
                    if (m_iFamilyID == (*iter)->m_iFamilyID)
                    {
                        //同一个家族的其他玩家也应该收到聊天信息
                        NotifyWords((*iter), tmpContent);
                    }
                }
            }
        }
    private:
        void NotifyWords(Fighter* otherPlayer, string tmpContent) //其他玩家收到了当前玩家的聊天信息
        {
            //显示信息
            cout << "玩家:" << otherPlayer->m_sPlayerName << "收到了玩家:" << m_sPlayerName << " 发送的聊天信息:" << tmpContent << endl;
        }

    private:
        int m_iPlayerID;  //玩家ID,全局唯一
        string m_sPlayerName; //玩家名字
        int m_iFamilyID;  //家族ID
    };

    //"战士"类玩家,父类为Fighter
    class F_Warrior :public Fighter
    {
    public:
        F_Warrior(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数
    };

    //"法师"类玩家,父类为Fighter
    class F_Mage :public Fighter
    {
    public:
        F_Mage(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数
    };

}

int main()
{
    //创建游戏玩家
    _nmsp1::Fighter* pplayerobj1 = new _nmsp1::F_Warrior(10, "张三"); //实际游戏中很多数据取自数据库。
    pplayerobj1->SetFamilyID(100); //假设该玩家所在的家族的家族ID是100
    _nmsp1::g_playerList.push_back(pplayerobj1); //加入到全局玩家列表中


    _nmsp1::Fighter* pplayerobj2 = new _nmsp1::F_Warrior(20, "李四");
    pplayerobj2->SetFamilyID(100);
    _nmsp1::g_playerList.push_back(pplayerobj2);

    _nmsp1::Fighter* pplayerobj3 = new _nmsp1::F_Mage(30, "王五");
    pplayerobj3->SetFamilyID(100);
    _nmsp1::g_playerList.push_back(pplayerobj3);

    _nmsp1::Fighter* pplayerobj4 = new _nmsp1::F_Mage(50, "赵六");
    pplayerobj4->SetFamilyID(200); //赵六和前面三人属于两个不同的家族
    _nmsp1::g_playerList.push_back(pplayerobj4);

    //当某个玩家聊天时,同族人都应该收到该信息
    pplayerobj1->SayWords("全族人立即到沼泽地集结,准备进攻!");

    //释放资源
    delete pplayerobj1;
    delete pplayerobj2;
    delete pplayerobj3;
    delete pplayerobj4;


}

 

 

namespace _nmsp2
{
    class Fighter; //类前向声明
    class Notifier //通知器父类
    {
    public:
        virtual void addToList(Fighter* player) = 0; //把要被通知的玩家加入到列表中
        virtual void removeFromList(Fighter* player) = 0; //把不想被通知的玩家从列表中去除
        virtual void notify(Fighter* talker, string tmpContent) = 0; //通知的一些细节信息
        virtual ~Notifier() {}
    };

    //玩家父类
    class Fighter
    {
    public:
        Fighter(int tmpID, string tmpName) :m_iPlayerID(tmpID), m_sPlayerName(tmpName) //构造函数
        {
            m_iFamilyID = -1; //-1表示没有加入任何家族
        }
        virtual ~Fighter() {} //析构函数

    public:
        void SetFamilyID(int tmpID) //加入家族的时候要设置家族ID
        {
            m_iFamilyID = tmpID;
        }
        int GetFamilyID() //获取家族ID
        {
            return m_iFamilyID;
        }

    public:
        void SayWords(string tmpContent,Notifier *notifier) //玩家说了某句话
        {
            notifier->notify(this, tmpContent);
        }
    
        //通知该玩家接收到其他玩家发送来的聊天信息,虚函数,子类可以覆盖以实现不同的功能
        virtual void NotifyWords(Fighter* talker, string tmpContent)
        {
            //显示信息
            cout << "玩家:" << m_sPlayerName << "收到了玩家:" << talker->m_sPlayerName << " 发送的聊天信息:" << tmpContent << endl;
        }

    private:
        int m_iPlayerID;  //玩家ID,全局唯一
        string m_sPlayerName; //玩家名字
        int m_iFamilyID;  //家族ID
    };

    //"战士"类玩家,父类为Fighter
    class F_Warrior :public Fighter
    {
    public:
        F_Warrior(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数
    };

    //"法师"类玩家,父类为Fighter
    class F_Mage :public Fighter
    {
    public:
        F_Mage(int tmpID, string tmpName) :Fighter(tmpID, tmpName) {} //构造函数
    };

    //聊天信息通知器
    class TalkNotifier :public Notifier
    {
    public:
        //将玩家增加到家族列表中来
        virtual void addToList(Fighter* player)
        {
            int tmpfamilyid = player->GetFamilyID();
            if (tmpfamilyid != -1) //加入了某个家族
            {
                auto iter = m_familyList.find(tmpfamilyid);
                if (iter != m_familyList.end())
                {
                    //该家族id在map中已经存在
                    iter->second.push_back(player); //直接把该玩家加入到该家族
                }
                else
                {
                    //该家族id在map中不存在
                    list<Fighter*> tmpplayerlist;
                    m_familyList.insert(make_pair(tmpfamilyid, tmpplayerlist)); //以该家族id为key,增加条目到map中
                    m_familyList[tmpfamilyid].push_back(player); //向该家族中增加第一个玩家
                }
            }
        }
        //将玩家从家族列表中删除
        virtual void removeFromList(Fighter* player)
        {
            int tmpfamilyid = player->GetFamilyID();
            if (tmpfamilyid != -1) //加入了某个家族
            {
                auto iter = m_familyList.find(tmpfamilyid);
                if (iter != m_familyList.end())
                {
                    m_familyList[tmpfamilyid].remove(player);
                }
            }
        }

        //家族中某玩家说了句话,调用该函数来通知家族中所有人
        virtual void notify(Fighter* talker, string tmpContent) //talker是讲话的玩家
        {
            int tmpfamilyid = talker->GetFamilyID();
            if (tmpfamilyid != -1) //加入了某个家族
            {
                auto itermap = m_familyList.find(tmpfamilyid);
                if (itermap != m_familyList.end())
                {
                    //遍历该玩家所属家族的所有成员
                    for (auto iterlist = itermap->second.begin(); iterlist != itermap->second.end(); ++iterlist)
                    {
                        (*iterlist)->NotifyWords(talker, tmpContent);
                    }
                }
            }
        }

    private:
        //map中的key表示家族id,value代表该家族中所有玩家列表
        map<int, list<Fighter*> > m_familyList;
    };
}


int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口

    //创建游戏玩家
    _nmsp2::Fighter* pplayerobj1 = new _nmsp2::F_Warrior(10, "张三"); //实际游戏中很多数据取自数据库。
    pplayerobj1->SetFamilyID(100); //假设该玩家所在的家族的家族ID是100
    
    _nmsp2::Fighter* pplayerobj2 = new _nmsp2::F_Warrior(20, "李四");
    pplayerobj2->SetFamilyID(100);
    
    _nmsp2::Fighter* pplayerobj3 = new _nmsp2::F_Mage(30, "王五");
    pplayerobj3->SetFamilyID(100);
    
    _nmsp2::Fighter* pplayerobj4 = new _nmsp2::F_Mage(50, "赵六");
    pplayerobj4->SetFamilyID(200); //赵六和前面三人属于两个不同的家族
    
    //创建通知器
    _nmsp2::Notifier* ptalknotify = new _nmsp2::TalkNotifier();

    //玩家增加到家族列表中来,这样才能收到家族聊天信息
    ptalknotify->addToList(pplayerobj1);
    ptalknotify->addToList(pplayerobj2);
    ptalknotify->addToList(pplayerobj3);
    ptalknotify->addToList(pplayerobj4);

    //某游戏玩家聊天,同族人都应该收到该信息
    pplayerobj1->SayWords("全族人立即到沼泽地集结,准备进攻!", ptalknotify);

    cout << "王五不想再收到家族其他成员的聊天信息了---" << endl;
    ptalknotify->removeFromList(pplayerobj3); //将王五从家族列表中删除
    pplayerobj2->SayWords("请大家听从族长调遣,前往沼泽地!", ptalknotify);

    //释放资源
    delete pplayerobj1;
    delete pplayerobj2;
    delete pplayerobj3;
    delete pplayerobj4;
    delete ptalknotify;



    return 0;
}

 

 

 

观察者(Observer)模式
    //(1)一个遍历问题导致的低效率范例
    //(2)引入观察者(Observer)模式
     //观察者设计模式 定义(实现意图):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于
        //它的对象都会自动得到通知。
    //发布-订阅(Publish-Subscribe);

    //观察者模式的四种角色
    //a)Subject(主题):观察目标,这里指Notifier类。
    //b)ConcreteSubject(具体主题):这里指TalkNotifier类。
    //c)Observer(观察者):这里指Fighter类。
    //d)ConcreteObserver(具体观察者):这里指F_Warrior和F_Mage子类。
    //观察者模式的特点:
    //a)在观察者和观察目标之间建立了一个抽象的耦合
    //b)观察目标会向观察者列表中的所有观察者发送通知。
    //c)可以通过增加代码来增加新的观察者或者观察目标,符合开闭原则
    //(3)应用联想
    //a)救援家族成员镖车
    //b)将新闻推荐给符合其胃口的读者
    //c)通过改变自身绘制的图形来真实的反应公司的销售数据。
    //d)炮楼只会对30米内的玩家(列表内玩家)进行攻击。

 

 
 
 
 

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,使得多个观察者对象能够监听某一个主题对象。当该主题对象状态发生变化时,它会通知所有观察者对象,使得它们能够自动更新自己。这个模式非常适合用于事件处理系统,例如GUI框架、通知系统等。

在C++中实现观察者模式通常涉及三个主要组件:

  1. 主题(Subject):维护一个观察者列表,提供注册和移除观察者的方法,以及通知所有观察者的方法。
  2. 观察者(Observer):定义一个接口或抽象类,以便接收通知。
  3. 具体主题(Concrete Subject) 和 具体观察者(Concrete Observer):具体的实现类。

下面是一个简单的C++实现观察者模式的例子:

1. 定义观察者接口

首先,定义一个纯虚类 Observer,作为所有观察者的基类:

cppCopy Code
// Observer.h
#ifndef OBSERVER_H
#define OBSERVER_H

class Observer {
public:
    virtual ~Observer() {}
    virtual void update(int value) = 0; // 纯虚函数,用于接收更新通知
};

#endif // OBSERVER_H

2. 定义主题类

然后,定义一个 Subject 类,包含注册、移除和通知观察者的方法:

cppCopy Code
// Subject.h
#ifndef SUBJECT_H
#define SUBJECT_H

#include <vector>
#include "Observer.h"

class Subject {
public:
    void attach(Observer* observer) {
        observers.push_back(observer);
    }

    void detach(Observer* observer) {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notify() {
        for (Observer* observer : observers) {
            observer->update(value);
        }
    }

    void setValue(int newValue) {
        value = newValue;
        notify();
    }

private:
    std::vector<Observer*> observers;
    int value;
};

#endif // SUBJECT_H

3. 实现具体观察者

创建具体的观察者类,实现 Observer 接口:

cppCopy Code
// ConcreteObserver.h
#ifndef CONCRETE_OBSERVER_H
#define CONCRETE_OBSERVER_H

#include <iostream>
#include "Observer.h"

class ConcreteObserver : public Observer {
public:
    void update(int value) override {
        std::cout << "ConcreteObserver: Value updated to " << value << std::endl;
    }
};

#endif // CONCRETE_OBSERVER_H

4. 使用观察者模式

最后,在主程序中创建主题和观察者,并进行交互:

cppCopy Code
// main.cpp
#include <iostream>
#include "Subject.h"
#include "ConcreteObserver.h"

int main() {
    Subject subject;

    ConcreteObserver observer1;
    ConcreteObserver observer2;

    subject.attach(&observer1);
    subject.attach(&observer2);

    subject.setValue(10);  // 通知所有观察者,值更新为10
    subject.setValue(20);  // 通知所有观察者,值更新为20

    subject.detach(&observer1);

    subject.setValue(30);  // 通知剩余的观察者,值更新为30

    return 0;
}

总结

以上代码展示了如何在C++中实现观察者模式。这个简单的实现可以根据实际需求进行扩展,比如添加更多的观察者类型、处理复杂的状态更新逻辑等。通过使用观察者模式,可以有效地实现对象之间的一对多依赖关系,从而实现松耦合的设计,提高代码的可维护性和可扩展性。

posted @ 2024-06-14 19:53  白伟碧一些小心得  阅读(49)  评论(0编辑  收藏  举报