• 设计模式观察者模式(KVO)


      观察者模式(Observer):观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

      举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。

      观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察对象。在刚才的例子中,业务数据是被观察对象,用户界面是观察者。观察者和被观察者之间存在“观察”的逻辑关联,当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应。如果在用户界面、业务数据之间使用这样的观察过程,可以确保界面和数据之间划清界限,假定应用程序的需求发生变化,需要修改界面的表现,只需要重新构建一个用户界面,业务数据不需要发生变化。

      观察者

      观察者(Observer)将自己注册到被观察对象(Subject)中,被观察对象将观察者存放在一个容器(Container)里。

      被观察者

      被观察者(subject)发生了某种变化,从容器中得到所有注册过的观察者,将变化通知观察者。

      

    C++实现demo。

         游戏中玩家数据和界面玩家UI之间必然会有关联,当玩家数据变化后,响应的UI也应该及时更新。

        两个虚基类:AbsSubject(被观察者),AbsObserver(观察者);    被观察者实现类:User(玩家数据类);

        观察者实现类:UserView(玩家信息面板类);HallView(大厅信息面板类)

        玩家信息面板和大厅信息面板都包含玩家昵称(name)和金币值(gold),当玩家金币值发生变化后,玩家(User)通知面板(UserView和HallView)也应发生响应变化。

    AbsSubject.h(被观察者):

     1 class AbsObserver;
     2 //被观察者,虚基类
     3 class AbsSubject
     4 {
     5 public:
     6     //ctor
     7     AbsSubject():m_bChanged(false) {};
     8     //pure dtor
     9     virtual ~AbsSubject() { removeAllObserver(); };
    10     //注册观察者
    11     void addObserver(AbsObserver* obser);
    12     //注销观察者
    13     void removeObserver(AbsObserver* obser);
    14     //注销所有观察者
    15     void removeAllObserver();
    16     //向所有观察者发送通知
    17     void postNotification(void*arg = NULL);
    18 
    19     //get observer count
    20     int getObserverCount() { return m_setObser.size(); }
    21     //状态是否变化
    22     bool hasChanged() { return m_bChanged; };
    23     //set changed true
    24     void setChanged() { m_bChanged = true; }
    25     //clear changed
    26     void clearChanged() { m_bChanged = false; }
    27     
    28     
    29 private:
    30     bool m_bChanged;//是否变化
    31     set<AbsObserver*> m_setObser;//观察者集合,set保证对象唯一
    32 };

    AbsSubject.cpp(被观察者):

     1 #include "stdafx.h"
     2 #include "AbsSubject.h"
     3 
     4 
     5 
     6 void AbsSubject::addObserver(AbsObserver * obser)
     7 {
     8     if (!obser)return;
     9     m_setObser.insert(obser);
    10 }
    11 
    12 void AbsSubject::removeObserver(AbsObserver * obser)
    13 {
    14     if (!obser)return;
    15     m_setObser.erase(obser);
    16 }
    17 
    18 void AbsSubject::removeAllObserver()
    19 {
    20     m_setObser.clear();
    21 }
    22 
    23 void AbsSubject::postNotification(void * arg)
    24 {
    25     if (!hasChanged())return;
    26     clearChanged();
    27     if (getObserverCount() == 0)return;//safe check
    28 
    29     set<AbsObserver*>::iterator itor = m_setObser.begin();
    30     do
    31     {
    32         (*itor)->update(this, arg);
    33         itor++;
    34     } while (itor != m_setObser.end());
    35 
    36 }

    AbsObserver(观察者):

     1 class AbsSubject;
     2 //观察者基类,纯虚基类
     3 class AbsObserver
     4 {
     5 public:
     6     //ctor
     7     AbsObserver() {};
     8     //dtor
     9     virtual ~AbsObserver() {};
    10     //pure virtual function.
    11     //当被观察者(subject)发生变化时,通知调用该方法
    12     virtual void update(AbsSubject* subject, void* arg) = 0;
    13 
    14 };

    被观察者实现类:User(玩家数据类)

     1 #pragma once
     2 
     3 //玩家数据,被观察者
     4 class User:public AbsSubject
     5 {
     6 public:
     7     //ctor
     8     User(string name);
     9     //dtor
    10     ~User();
    11     
    12     //增加gold
    13     void addGold(int gold);
    14 
    15     //广播通知
    16     void post(const string& content);
    17 
    18 public:
    19     //get name
    20     string getName() { return m_strName; }
    21     //get gold;
    22     int getGold() { return m_iGold; }
    23 
    24 private:
    25     string m_strName;//昵称
    26     int m_iGold;//金币
    27 
    28 };
    29 
    30 
    31 
    32 User::User(string name) 
    33     :m_strName(name),
    34     m_iGold(100)
    35 {
    36 }
    37 
    38 
    39 User::~User()
    40 {
    41 }
    42 
    43 void User::addGold(int gold)
    44 {
    45     if (gold == 0)return;
    46     m_iGold += gold;
    47     m_iGold = m_iGold < 0 ? 0 : m_iGold;//safe check
    48 
    49     post(m_strName);
    50 }
    51 
    52 void User::post(const string & content)
    53 {
    54     setChanged();
    55     postNotification(const_cast<char*>(content.c_str()));
    56     
    57 }

      观察者实现类:UserView(玩家信息面板类):

     1 #pragma once
     2 
     3 //玩家界面,观察者
     4 class UserView:public AbsObserver
     5 {
     6 public:
     7     //ctor
     8     UserView(User* user) :m_user(user){ user->addObserver(this); }
     9     //dtor
    10     ~UserView() { m_user->removeObserver(this); }
    11     //当被观察者(subject)发生变化时,通知调用该方法
    12     virtual void update(AbsSubject* subject, void* arg);
    13 
    14 private:
    15     //string m_strName;//user name
    16     User* m_user;//user
    17 };
    18 
    19 void UserView::update(AbsSubject * subject, void * arg)
    20 {
    21     char* content = static_cast<char*>(arg);
    22     if (dynamic_cast<User*>(subject))
    23     {
    24         cout << "UserView::update From User=" << m_user->getName() << ",gold=" << m_user->getGold() << ",Arg=" << content << endl;
    25     }
    26     else
    27     {
    28         cout << "UserView::update From XXX=" << m_user->getName() << ",gold=" << m_user->getGold() << ",Arg=" << content << endl;
    29     }
    30 }

    HallView(大厅信息面板类):

     1 #pragma once
     2 //大厅界面,观察者
     3 class HallView:public AbsObserver
     4 {
     5 public:
     6     //ctor
     7     HallView(User* user):m_user(user) { user->addObserver(this); }
     8     //dtor
     9     ~HallView() { m_user->removeObserver(this); };
    10     //当被观察者(subject)发生变化时,通知调用该方法
    11     virtual void update(AbsSubject* subject, void* arg);
    12 
    13 private:
    14     User* m_user;//玩家
    15     
    16 };
    17 
    18 void HallView::update(AbsSubject * subject, void * arg)
    19 {
    20     char* content = static_cast<char*>(arg);
    21     if (dynamic_cast<User*>(subject))
    22     {
    23         cout << "HallView::update From User="<<m_user->getName()<<",gold=" << m_user->getGold()<<",arg="<<content<< endl;
    24     }
    25     else
    26     {
    27         cout << "HallView::update From XXX=" << m_user->getName() << ",gold=" << m_user->getGold()<<",arg=" << content << endl;
    28     }
    29 }

        main()测试:

     1 int main()
     2 {
     3     //玩家(被观察者)
     4     User* user = new User("孟栋");
     5 
     6     //玩家面板(观察者1)
     7     UserView* userView = new UserView(user);
     8     //大厅面板(观察者2)
     9     HallView* hallView = new HallView(user);
    10 
    11     
    12     
    13     //玩家增加50金币,玩家面板和大厅面板会更新玩家UI信息
    14     user->addGold(50);
    15 
    16     //玩家减少30金币,玩家面板和大厅面板会更新玩家UI信息
    17     user->addGold(-30);
    18 
    19     return 0;
    20 }

         打印:

    UserView::update From User=孟栋,gold=150,Arg=孟栋
    HallView::update From User=孟栋,gold=150,arg=孟栋
    UserView::update From User=孟栋,gold=120,Arg=孟栋
    HallView::update From User=孟栋,gold=120,arg=孟栋

    多加点评!谢谢

  • 相关阅读:
    JavaScript高级第01天笔记
    JavaScript基础第06天笔记
    JavaScript基础第05天笔记
    JavaScript基础第04天笔记
    JavaScript基础第03天笔记
    JavaScript基础第02天笔记
    JavaScript基础第01天笔记
    03-CSS文字文本样式
    CSS第二天笔记
    浮动
  • 原文地址:https://www.cnblogs.com/mengdongsky/p/6831173.html
Copyright © 2020-2023  润新知