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

 

多加点评!谢谢

 

posted @ 2017-05-09 16:27  孟栋sky  阅读(554)  评论(0编辑  收藏  举报