C++设计模式——观察者模式(observer pattern)

一、原理讲解

1.1意图

定义一种“一对多”的关系。当一个对象(被观察者/发布者)的状态发生改变时,所有依赖它的对象都将得到通知并更新。又有别名为发布-订阅(publish-subscribe)。

1.2应用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中,以使它们可以独立的改变和复用
  • 当一个对象改变,连带其它对象也跟着改变,但是不需要知道具体改变对象的数量

1.3代码实现步骤

a1 先实现一个抽象主题接口类ISubject,主题接口提供增加、删除、通知接口

a2 在实现一个抽象观察者接口类IObserver,观察者接口提供更新操作接口

a3 实现两个具体观察者类,继承观察者接口类,实现具体更新函数update()

a4 实现一个具体的主题类,继承抽象接口类,实现具体的增加、删除、通知观察者函数细节。

二、实现代码

#include <iostream>
#include <list>

using namespace std;

class IObserver;

//主题接口,抽象基类
class ISubject {	
public:
	virtual ~ISubject(){}

	virtual void add(IObserver *pObserver) = 0;
	virtual void remove(IObserver *pObserver) = 0;
	virtual void notify() = 0; //通知

protected:
	ISubject() {}
};

//观察者接口,抽象基类
class IObserver
{
public:	
	virtual ~IObserver() {}

	virtual void update() = 0; //更新接口

protected:
	IObserver(){}
};

//具体观察者,实现具体细节函数,比如更新函数update(){...}
class Observer1 : public IObserver
{
public:
	Observer1(){}
	~Observer1(){}

	void update() { //观察者得到通知并执行更新函数
		cout << "run Observer1 function update()" << endl;
	}
};

//具体观察者,实现具体细节函数,比如更新函数update(){...}
class Observer2 : public IObserver
{
public:
	Observer2() {}
	~Observer2() {}

	void update() { //观察者得到通知并执行更新函数
		cout << "run Observer2 function update()" << endl;
	}
};

//核心操作在具体主题类这里,实现增加、删除、通知观察者和更新
class Subject : public ISubject
{
public:
	Subject(){}
	~Subject(){}

	virtual void add(IObserver *pObserver) {
		observerList.push_back(pObserver);
	}

	virtual void remove(IObserver *pObserver) {
		observerList.remove(pObserver);
	}

	virtual void notify() { //实现细节,通知观察者并更新
		for (std::list<IObserver*>::iterator iter = observerList.begin(); iter != observerList.end(); iter++) {
			(*iter)->update();
		}
	}

	void setState(int state) {
		m_state = state;		
	}

private:
	std::list<IObserver *> observerList;
	int m_state; //状态值,如果发生改变,表示状态改变了,需要通知观察者并更新
};

int main()
{
	Subject *subject = new Subject();
	Observer1 *oberver1 = new Observer1();
	Observer2 *oberver2 = new Observer2();

	//调用流程:改变状态->注册观察者1和观察者2->通知观察者
	subject->setState(1); //状态第一次发生改变
	subject->add(oberver1); //添加/注册观察者1
	subject->add(oberver2); //添加观察者2
	subject->notify();
	
	subject->setState(2); //状态第二次发生改变
	subject->remove(oberver2); //删除观察者2
	subject->notify(); //发布消息通知

	delete subject;
	delete oberver1;
	delete oberver2;

	getchar();
	return 1;
}

三、总结

3.1代码实现解读

步骤a4的本质在于主题接口类组合一系列观察者类对象,也即是观察者类对象列表。通过在主题类实现具体细节,进行观察者的增加、删除、通知更新(也就是遍历观察者列表对象并调用其更新函数)

步骤a4是类的组合和继承的一种巧妙用法,继承主题抽象接口类,组合抽象观察者基类,在运行时动态调用观察者update()函数,也就是将update()的实现细节放在了子类中实现!!!

3.2功能总结

抽象被观察者(也称为目标)(subject)、具体被观察者(也称为具体目标)(concreteSubject),抽象观察者(object)、具体观察者(concreteObserver)的功能总结如下。

3.2.1  subject(抽象目标,或者是抽象接口类)

  • 目标知道它的观察者。可以 有任意多个观察者观察同一目标。
  • 提供注册(增加)和删除观察者的接口

3.2.2  observer(抽象观察者)

  • 定义一个更新接口,当目标对象改变时,就会通知到这个接口

3.2.3  concreteSubject(具体目标)

将相关状态存入具体观察者(concreteObserver)

当自身状态发生改变时,通知每一个注册了的观察者

3.2.4  concreteObserver(具体观察者)

  • 维护一个指向具体目标(concreteSubject)对象的应引用
  • 存储有关状态,这些状态应与目标的状态保持一致
  • 实现抽象观察者(observer)的更新接口,以使自身状态与目标状态保持一致

3.3满足设计模式八大设计原则

a1依赖导致原则(DIP):高层模块不应该依赖于底层模块(变化),二者都应该依赖与抽象(稳定);

a2开闭原则(OCP):对扩展开发(比如本示例中的不同算法),对更改封闭(比如本例中的run()函数);

a6优先使用类对象组合,而不是类继承:子类对象只要求被组合对象具有良好定义的借口,耦合度低;

a8针对接口编程,而不是针对实现编程:不将变量申明为某个特定的类,而声明为某个接口。客户无需知道具体类型,只需要知道特定接口。减少系统依赖关系,实现高内聚、松耦合。

 

 

参考内容:

https://www.cnblogs.com/carsonzhu/p/5770253.html(参考:观察者模式(重点参考原理和代码!!!))

https://blog.csdn.net/zhouzhenhe2008/article/details/74784277(参考:观察者模式(参考原理讲解))

https://blog.csdn.net/qq_36391130/article/details/82939799(参考:设计模式八大设计原则)

https://blog.csdn.net/i_chaoren/article/details/80561204(参考:观察者模式)

https://www.jianshu.com/p/1b1def6960a4(参考:观察者模式)

https://www.bilibili.com/video/av22292899?from=search&seid=8813426322713310552(参考:哔哩哔哩C++设计模式!!!)

Erich Gamma,Richard Helm.《设计模式 可复用面向对象软件的基础》[M].机械工业出版社,2019:219-227

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章