• 设计模式观察者模式(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=孟栋

    多加点评!谢谢

  • 相关阅读:
    HTML DOM 06 节点关系
    HTML DOM 05 事件(三)
    HTML DOM 05 事件(二)
    HTML DOM 05 事件(一)
    html DOM 04 样式
    html DOM 03 节点的属性
    html DOM 02 获取节点
    html DOM 01 节点概念
    JavaScript 29 计时器
    JavaScript 28 弹出框
  • 原文地址:https://www.cnblogs.com/mengdongsky/p/6831173.html
Copyright © 2020-2023  润新知