观察者模式

1 定义

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

2 UML类图及核心思想

在这里插入图片描述

观察者模式也叫做发布-订阅模式,比如我关注了某微信公众号,那么一旦公众号维护者进行了内容更新,就会把消息推送到各订阅者。当然如果订阅者感觉推送内容质量一般,则可以取消关注。

主题内部维护者一个观察者的列表
观察者内部也拥有一个主题对象的指针或引用
所有的观察者都实现了统一的接口(这里是update接口),以接收主题的数据更新
如果观察者想订阅某服务,则在自己的构造函数内将自己添加到主题的观察者列表中,这是通过调用主题的Attach接口完成的;
如果观察者不想再订阅某项服务,则观察者从可以调用主题的Detach函数将自己从主题的观察者列表中删除
具体的主题在进行新消息通知时,遍历当前的观察者列表,挨个调用观察者的update函数,把新消息推送给各观察者。

什么时候用观察者模式?
当一个对象的改变需要同时改变其他对象的时候,而且它不知道有多少对象需要改变,就需要考虑使用观察者模式。

为什么有抽象主题和抽象观察者的存在?
因为具体的主题可以有多个,比如公众号的消息可以是员工A发布的,也可以是员工B发布的,如果不引入抽象主题,那么观察者就需要在具体主题改变时修改代码,这样违背了开闭原则。对于主题而言同样如此,添加了新类型的观察者时也需要修改代码。

按照“针对接口编程,而不是针对实现编程的原则”,主题包含观察者的基类对象,这样主题就可以把消息推送给不同类型的观察者。观察者也包含主题的基类对象。

解除耦合
观察者模式所做的工作实际上是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得一边的变换不会影响到另一边。

数据更新时的“推”和“拉”
数据更新时,如果由主题遍历观察者列表中已注册的所有观察者,调用其update函数更新数据,是一种“推”的实现方式。这种方式的缺点是,如果主题推送的数据有所扩充,需要挨个修改推送给不同观察者的数据内容,代码可扩充性较差。

应该改为通过“拉”的方式进行数据更新,所谓“拉”即各观察者自己按照自己的需要从主题中获取数据,当然也需要主题提供了一系列的get接口允许观察者从自身获取不同类型的数据。这样的好处是,如果新增了观察者,或者某已有观察者想获取的数据发生了变化,主题的代码不需要修改,只需要修改发生了变化的或新增的观察者的实现即可,代码修改相对较少。

3 示例代码

//subject.h

#include <iostream>
#include<list>
#include "Observer.h"

class Subject
{

public:
	Subject() {
		
	}

	virtual ~Subject() {

	}
	std::list<Observer*> m_Observer;

	virtual void Attach(Observer* pObserver) =0;
	virtual void Detach(Observer* pObserver) =0;
	virtual void Notify() =0;
};

//ConcreteSubject.h
#include "Subject.h"

class ConcreteSubject : public Subject
{

public:
	ConcreteSubject();
	virtual ~ConcreteSubject();

	virtual void Attach(Observer* pObserver);
	virtual void Detach(Observer* pObserver);
	virtual void Notify();

};

//ConcreteSubject.cpp
#include "ConcreteSubject.h"


ConcreteSubject::ConcreteSubject()
{

}



ConcreteSubject::~ConcreteSubject(){

}





void ConcreteSubject::Attach(Observer* pObserver)
{
	m_Observer.push_back(pObserver);
}


void ConcreteSubject::Detach(Observer* pObserver)
{
	for (std::list<Observer*>::iterator iter = m_Observer.begin(); iter != m_Observer.end(); )
	{
		if (*iter == pObserver)
		{
			std::cout << "Delete " << (*iter)->m_strName << std::endl;
			iter = m_Observer.erase(iter);
			break;
		}
		else
		{
			iter++;
		}
			
	}
}


void ConcreteSubject::Notify()
{
	for (std::list<Observer*>::iterator iter = m_Observer.begin(); iter != m_Observer.end(); iter++)
	{
		(*iter)->Update();
	}
}

//Observer.h
class Observer
{

public:
	Observer(const char* pName) {
		m_strName = pName;
	}

	virtual ~Observer() {

	}

	virtual void Update() =0;

	const char* m_strName;

};

//ConcreteObserver.h
#include "Subject.h"
#include "Observer.h"

class ConcreteObserver : public Observer
{

public:
	ConcreteObserver(const char* pName,Subject* pSubject);
	virtual ~ConcreteObserver();

	virtual void Update();
};

//ConcreteObserver.cpp
#include "ConcreteObserver.h"

ConcreteObserver::ConcreteObserver(const char* pName, Subject* pSubject):Observer(pName)
{
	pSubject->Attach(this);
}

ConcreteObserver::~ConcreteObserver(){

}

void ConcreteObserver::Update(){
	std::cout << "I am " << m_strName << ",I accept new information!" << std::endl;
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值