首页 > 编程语言 >c/c++设计模式---观察者模式

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

时间:2024-06-14 19:55:04浏览次数:28  
标签:Fighter Observer void 家族 观察者 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++中实现观察者模式。这个简单的实现可以根据实际需求进行扩展,比如添加更多的观察者类型、处理复杂的状态更新逻辑等。通过使用观察者模式,可以有效地实现对象之间的一对多依赖关系,从而实现松耦合的设计,提高代码的可维护性和可扩展性。

标签:Fighter,Observer,void,家族,观察者,c++,玩家,---,设计模式
From: https://www.cnblogs.com/bwbfight/p/18248553

相关文章

  • [NeurIPS2021]Open-set Label Noise Can Improve Robustness Against Inherent Label
    这篇文章与ICML2022的Open-sampling是同一个作者,方法一模一样,只是问题的场景变为噪声标签学习,Open-sampling是长尾问题的场景,可参见写的这篇blog。这两篇文章大致做法完全相同:对biased数据集引入开集数据,在每个epoch分配均匀的闭集标签。如果是longtaileddata,还涉及不平衡问题,......
  • 前端使用 Konva 实现可视化设计器(15)- 自定义连接点、连接优化
    前面,本示例实现了折线连接线,简述了实现的思路和原理,也已知了一些缺陷。本章将处理一些缺陷的同时,实现支持连接点的自定义,一个节点可以定义多个连接点,最终可以满足类似图元接线的效果。请大家动动小手,给我一个免费的Star吧~大家如果发现了Bug,欢迎来提Issue哟~github源码g......
  • uni-app项目button组件去不掉的灰色边框爬坑
    前情uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE让开发体验非常棒,公司项目就是主推uni-app。坑位最近在开发个人中心的时候,需要用到微信头像和昵称的,微信的用户信息获取经过了几番调整,目前已不再提供获取用户头像和昵称的......
  • VR-LLM-AAC
    VR-LLM-AAC方案测试测试一:汉字聚类hanzi_similar算法GithubKmeans算法hanzi_similar通过四角编码,汉字结构,偏旁部首,笔画数来判断两个汉字之间的相似度将权重调整为调高偏旁部首和汉字结构的权重根据任意两个汉字之间的相似度,通过Kmeans算法构建相似度矩阵,取得......
  • 设计模式之适配器模式
    设计模式中的适配器模式一、概述适配器模式(AdapterPattern)是一种结构型设计模式,它用于将一个类的接口转换成客户端所期望的另一个接口,使原本因接口不兼容而无法协同工作的类能够一起工作。适配器模式分为类适配器模式和对象适配器模式两种。二、主要角色目标接口(Target):客......
  • LVS负载均衡集群企业级应用实战-LVS-DR(四)
    目录LVS-DR 一.环境准备二.对虚拟主机操作三.对真实服务器操作 四.打开网页测试LVS-DR 一.环境准备三台虚拟机,都要在同一网段内,统一关闭防火墙和selinux,时间同步,配置好YUM源。系统用centos和roucky都行。主机名主机IP模拟服务器系统用途localhostVIP:1......
  • DreamJudge-1217-国名排序
    1.题目描述TimeLimit:1000msMemoryLimit:256mb问题描述:小李在准备明天的广交会,明天有来自世界各国的客房跟他们谈生意,小李要尽快的整理出名单给经理,你能帮他把客户来自的国家按英文字典次序排好吗?例如小李手上有来自加拿大,美国,中国的名单,排好的名单应是美国,加拿......
  • 数据结构(C/C++)专题一:顺序表与链表
    今天开始一个新的专题:数据结构当然,不仅仅适用于学习也适用于408考研。那么提起数据结构思维导图:总结如下:·1.初识顺序表与链表首先呢我们要明白,数据结构有物理结构,也有逻辑结构物理结构就是电脑实际的结构,链式,非链式,索引,散列eg:链式结构(LinkedStructure)例子:火车车......
  • MySQL入门学习-聚合和分组.子查询.相关子查询
        在MySQL中,子查询是指在一个查询语句中嵌套另一个查询语句。子查询可以分为相关子查询和非相关子查询两种类型。    相关子查询是指子查询的执行结果依赖于外部查询中的值。在执行相关子查询时,MySQL会先执行外部查询,然后根据外部查询的结果来执行子查询......
  • 记录--N 个值得一看的前端代码片段
    ......