观察者模式理解
观察者模式是一种行为设计模式,也叫发布订阅模式。它定义了一种一对多的依赖关系,让多个观察者同时监听一个主题对象,当主题对象状态发生变化,会自动通知所有的观察者更新状态。
可以理解为班长为其他学生放哨,当得知班主任要来教室的时候,通知其他同学,其他同学立即进入学习状态;也可以理解为发布订阅的形式,当发布者发布主题后,订阅了这个主题的所有订阅者便可以收到该消息。
下面是观察者模式下的两个示例。
观察者模式的示例1
开发环境:vs2017下控制台程序
代码
其中放哨的同学被定义为被观察者,即Observed,其他同学被定义为观察者,即Observer。此项目中只有一个ObserverPattern.cpp文件。
ObserverPattern.cpp
// ObserverPattern.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <list>
using namespace std;
/*
观察者模式
一个被观察者,多个观察者(一对多的关系)观察者的动作取决与被观察者,也就是被观察者添加所有观察者后,
观察的事情发生变化会通知所有的观察者,所有观察者做出响应
*/
class Observed;
//观察者
class Observer
{
public:
Observer(Observed *observer = nullptr)
{
m_observer = observer;
}
~Observer()
{
//if (m_observer)//谁开辟谁释放的原则
//{
// delete m_observer;
// m_observer = nullptr;
//}
}
void update(const string & action)
{
cout << "action:" << action << endl;
cout << "反应动作" << endl;
}
private:
Observed *m_observer;
};
//被观察者
class Observed
{
public:
Observed()
{
m_observerList.clear();
}
~Observed()
{
//list<Observer*>::iterator it = m_observerList.begin();//delete的时候已经被释放,不需要再释放
//for (; it != m_observerList.end(); ++it)
//{
// if (*it)
// {
// delete *it;
// *it = nullptr;
// }
//}
}
void notify(const string & notifyInfo)
{
for (auto &var:m_observerList)
{
var->update(notifyInfo);
}
}
void addObserver(Observer *obj)
{
m_observerList.push_back(obj);
}
void detachObserver(Observer *obj)
{
for (auto & var:m_observerList)
{
if (obj == var)
{
m_observerList.remove(var);
break;
}
}
}
private:
list<Observer*> m_observerList;
};
int main()
{
Observed *oberser = new Observed;
Observer *obj1 = new Observer(oberser);
Observer *obj2 = new Observer(oberser);
oberser->addObserver(obj1);
oberser->addObserver(obj2);
oberser->notify("敌人来了");
delete oberser;
oberser = nullptr;
delete obj1;
obj1 = nullptr;
delete obj2;
obj2 = nullptr;
return 0;
}
注意点
以上代码中我注释掉的部分都为所犯错误点,这里做详细说明。
注释点一
在析构函数中注释掉以下代码。
//if (m_observer)//谁开辟谁释放的原则
//{
// delete m_observer;
// m_observer = nullptr;
//}
上述的ObserverPattern.cpp如果放开析构函数中注释掉的代码,在运行的时候会出现奔溃,主要因为main函数中调用了delete oberser;oberser = nullptr;析构了 被观察者Observed,而Observed类的变量作为观察者Observer 类的成员变量m_observer,当执行delete obj1;obj1 = nullptr;时会调用Observer类的析构函数,此时如果不注释掉上述析构函数中的代码,会导致当执行delete oberser;oberser = nullptr;后已经释放了被观察者指向的内存,又会执行一遍释放被观察者指向的内存,也就是对一块已经释放的内存再次释放了,所以会崩溃。本着谁开辟谁释放,上述ObserverPattern.cpp中析构函数内的释放内存那块应该注释掉。
注释点二
在被观察者类Observed的析构函数中注释掉释放链表中的观察者对象指针的代码。
~Observed()
{
//list<Observer*>::iterator it = m_observerList.begin();//delete的时候已经被释放,不需要再释放
//for (; it != m_observerList.end(); ++it)
//{
// if (*it)
// {
// delete *it;
// *it = nullptr;
// }
//}
}
被观察者类Observed有一个成员变量list<Observer*> m_observerList,上述代码看似没有问题,但是与主函数main中的代码
Observer *obj1 = new Observer(oberser);
Observer *obj2 = new Observer(oberser);
一起来看,就显得上述析构函数中的释放观察者指针就没有必要了,本着谁开辟谁释放的原则,obj1,obj2采用main函数中delete的方式释放,被观察者类Observed的成员变量list<Observer*> m_observerList中的观察者指针就会被释放。所以Observed类的析构函数链表中的元素被释放应该被注释。
观察者模式的示例2
使用发布者与订阅者对象,抽象类为基类,派生类派生于抽象基类。发布者扮演放哨者的角色,订阅者扮演观察者的角色,也就是被通知的对象,下面为具体的代码。
该部分代码相比示例1要更上一个档次,首先使用了C++11中的智能指针,可以自动回收资源,另外采用抽象类做为基类,派生类继承后重写虚函数,并增加自己的行为与属性,封装性上也比示例1也更好。
// ObserverPattern2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <memory>
#include <string>
#include <list>
using namespace std;
/*
观察者模式
智能指针实现观察者模式
*/
class BaseObeserve
{
public:
virtual ~BaseObeserve() {};
virtual void update(const string & info) = 0;
};
class BaseObserved
{
public:
virtual ~BaseObserved(){}
virtual void attach(shared_ptr<BaseObeserve> obeserve) = 0;
virtual void detach(shared_ptr<BaseObeserve> observe) = 0;
virtual void notify(const string &info) = 0;
};
class publish :public BaseObserved
{
public:
void attach(shared_ptr<BaseObeserve> obeserve) override
{
m_observerList.push_back(obeserve);
}
void detach(shared_ptr<BaseObeserve> observe)override
{
m_observerList.remove(observe);
}
void notify(const string &info)override
{
for (auto & observe:m_observerList)
{
cout << "notify:注意了" << endl;
observe->update(info);
}
}
void createMessage(const string &info) //相当于创建主题
{
m_info = info;
notify(m_info);
}
private:
string m_info;
list<shared_ptr<BaseObeserve>> m_observerList;
};
class subscriber :public BaseObeserve
{
public:
subscriber(shared_ptr<BaseObserved> observed) :
m_observed(observed)
{
m_observed->attach(make_shared<subscriber>(*this));
m_count = m_number;
}
void update(const string & info)override
{
m_mesg = info;
cout << "info= " << info << endl;
}
void removeFromList()
{
m_observed.get()->detach(make_shared<subscriber>(*this));
}
private:
string m_mesg;
static int m_number;
int m_count;
shared_ptr<BaseObserved> m_observed;
};
int subscriber::m_number = 2;
void fun()
{
shared_ptr<publish> pub = shared_ptr<publish>(new publish());
shared_ptr<subscriber> sub1 = shared_ptr<subscriber>(new subscriber(pub));
shared_ptr<subscriber> sub2 = shared_ptr<subscriber>(new subscriber(pub));
pub.get()->createMessage("敌人来了");
//释放m_observerList中指针指向的内存
sub1.get()->removeFromList();
sub2.get()->removeFromList();
}
int main()
{
fun();
return 0;
}
示例三
该示例比较简洁,有助于理解,看这一个示例就可以。
示例代码
下面直接上代码:
//观察者
class Observer
{
public:
virtual void update() = 0;
};
class ObserverA :public Observer //观察者A
{
public:
void update() override
{
cout << "更新ObserverA!" << endl;
}
};
class ObserverB :public Observer//观察者B
{
public:
void update() override
{
cout << "更新ObserverB!" << endl;
}
};
class Subject //主题(被观察者)
{
public:
void registerObserver(Observer *observer)//注册观察者
{
if(!observer)
{
cout << "观察者为空,无法注册!" << endl;
}
m_observer.push_back(observer);
}
void removeObserver(Observer *pObserver) //移除观察者
{
for (auto it = m_observer.begin();it != m_observer.end();++it)
{
if (*it == pObserver)
{
m_observer.erase(it);
break;
}
}
}
void notifyObserver() //通知所有的观察者
{
for (auto &observer:m_observer)
{
observer->update();//观察者更新状态
}
}
private:
vector<Observer*> m_observer;
};
测试代码
这里省略头文件。
int main()
{
Subject subject;
ObserverA observer1;
ObserverB observer2;
subject.registerObserver(&observer1);
subject.registerObserver(&observer2);
subject.notifyObserver();
subject.removeObserver(&observer2);
subject.notifyObserver();
return 0;
}
运行结果
应用场景
观察者模式主要应用场景包括:
-
当对象之间存在一对多的关系时,且一个对象的改变会影响其他多个对象时,可以使用观察者模式。
-
当系统需要在不同层次上将一个对象分离成多个类时,使用观察者模式可以降低系统的耦合度,并提高系统的可扩展性与可维护性。
-
当系统的某个对象状态发生改变后,需要及时通知其他相关对象时,使用观察者模式可以提高系统的响应速度与实时性。
-
当需要在一个对象状态改变时,采取一些特定的操作时,使用观察者模式可以简化系统设计,让设计更加符合“开闭原则”。
-
当需要增加新的观察者对象或者删除现有的观察者对象时,使用观察者模式可以方便地进行扩展,无需修改现有代码。
总之,观察者模式的应用场景和其概念基本是相通的,还是重在理解。
注意点
观察者模式分为被观察者和观察者,被观察者就相当于放哨的人,观察者相当于观察者,被观察者通知观察者相应的事情,其他所有观察者响应通知。被观察者有一个notify函数,观察者有一个update函数来响应通知,同时需要将所有的观察者加入到被观察者的成员变量list中,这就需要增加观察者,和删除观察者函数。其观察者模式还需要仔细领悟。