设计模式笔记——观察者模式(Observer)

设计模式笔记——观察者模式(Observer)

阅读设计模式-可复用面向对象软件的基础一书做的笔记。

意图

建立一个一对多的对象关系,当一发生了变化,多就都得到通知并且进行一个更新的操作。

别名

依赖(dependent),发布-订阅(publish-subscrib)。

动机

当一个系统中有多个相互协作的,类需要维护相关对象之间的一致性时,就会出现许多紧密的耦合,导致了可复用性的降低。

在设计模式-可复用面向对象软件的基础一书中所提到的例子,书中表述了将数据与表格之间独立工作又相互关联的一种关系,当目标数据发生改变时着会通知其他的表格一同发生改变,保持数据与表格展示的状态一致,在这里 数据就是目标(subject),表格着是观察者(Observer)。

观察者模式(Observer)就是描述了如何建立这种关系,一个目标发生改变则可以通过发送通知去告诉该目标的观察者,每个观察者收到通知后都将自身状态于目标状态保持同步。

同时也可以称为发布-订阅(publish-subscrib),目标是发布者,它发出的通知并不需要知道谁是观察者,可以有多个观察者订阅并接受通知。

图片来源设计模式-可复用面向对象软件的基础书中第二百二十页
来源设计模式-可复用面向对象软件的基础

适用性

以下情况适用观察者模式:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一方面。将二者独立封装,使它们可以各自独立地改变和复用。
  • 当一个对象发生改变时引发其他对象一起改变,而不知道有多少对象需要改变。
  • 一个对象必须通知其他对象,但是它又不能假定其他对象是谁,换句话说就是不希望这些对象紧耦合。

结构

和书中的图一样,但删掉了一些其他的东西。
在这里插入图片描述

参与者

  • 抽象类
    • Subject(目标)
      • 目标知道它的观察者,可以有任意多个观察者观察同一目标。
      • 提供注册和删除的观察者对象的接口。
    • Observer(观察者)
      • 当目标发生改变时需要获取通知的对象定义一个更新的接口
  • 实现类
    • ConcreteSubject(具体目标)
      • 保存状态
      • 当状态发生改变时发送通知给观察者
    • ConcreteObserver(具体观察者)
      • 维护一个指向ConcreteSubject对象的引用
      • 存储目标有关的状态(数据),这个状态(数据)和观察目标一致
      • 实现Observer的更新接口,保证自身和目标状态(数据)一致

代码实现

代码来源 https://refactoringguru.cn/design-patterns/observer/cpp/example 做了个精简,删掉了一部分打印,打印太多东西了看着花,顺带加上了注释看的明白点。

// 观察者
class Observer
{
public:
	virtual ~Observer() {}
	// 更新
	virtual void update(const std::string& message) = 0;
};

// 目标
class Subject
{
public:
	virtual ~Subject() {};
	// 注册观察者
	virtual void attach(Observer* observer) = 0;
	// 删除观察者
	virtual void detch(Observer* observer) = 0;
	// 通知
	virtual void notify() = 0;
};

// 具体目标
class ConcreteSubject : public Subject
{
public:
	virtual ~ConcreteSubject()
	{
		std::cout << "具体目标析构\n";
	}
	// 增添观察目标的观察者,将其加入观察者列表中
	void attach(Observer* observer) override {
		m_obseverList.push_back(observer);
		std::cout << "观察者+1\n";
	}

	// 删除观察者
	void detch(Observer* observer)override {
		m_obseverList.remove(observer);
		std::cout << "观察者-1\n";
	}

	// 发送通知
	void notify() override {
		for (auto i : m_obseverList) {
			// 将改变的数据更新至观察者中
			i->update(m_message);
		}
	}

	// 改变数据
	void setMessage(std::string message = "无数据") {
		this->m_message = message;
		// 数据发生改变,发送通知
		this->notify();
	}


private:
	std::list<Observer*> m_obseverList; // 观察者列表,维护多个观察者,以便发送通知
	std::string m_message; // 数据,目标要处理的数据,可以是其他数据
};

// 具体观察者
class ConcreteObserver :public Observer {
public:
	// 在构造函数中直接传入要观察的目标的引用,使用一个引用来维护具体目标
	ConcreteObserver(ConcreteSubject& subject):m_subject(subject) {
		// 同时吧当前观察者记录至目标中
		this->m_subject.attach(this);
	}
	~ConcreteObserver()
	{
		// 删除观察者时也将其从列表中移除
		this->m_subject.detch(this);
	}
	// 更新观察者中的数据,同步与目标一致
	void update(const std::string& message)override {
		m_message = message;
		printMessage();
	}

	// 打印数据,理解为view层展示数据
	void printMessage() {
		std::cout << "展示目标当前的数据:" << m_message << "\n";
	}

private:
	// 要观察的目标引用
	ConcreteSubject& m_subject;
	// 数据,与目标数据保持一致
	std::string m_message;
};

void client()
{
	// 目标
	std::shared_ptr<ConcreteSubject>  subject = std::make_shared<ConcreteSubject>();

	// 观察者,不要像我这样使用智能指针,我只是懒得写delete所以用智能指针
	std::shared_ptr <ConcreteObserver> observer_1 
			= std::make_shared<ConcreteObserver>(*(subject.get()));
	std::shared_ptr <ConcreteObserver> observer_2 
			= std::make_shared<ConcreteObserver>(*(subject.get()));
	std::shared_ptr <ConcreteObserver> observer_3 
			= std::make_shared<ConcreteObserver>(*(subject.get()));

	// 更新目标数据
	subject->setMessage("典,孝");
	std::cout << "------------------------------------\n";
	// 添加区域,当智能指针离开这个区域时自动析构,测试删除观察者
	{
		std::shared_ptr <ConcreteObserver> observer_4 
			= std::make_shared<ConcreteObserver>(*(subject.get()));
		subject->setMessage("麻,乐,绷");
	}
	std::cout << "------------------------------------\n";
	subject->setMessage("批,赢");
}

int main() {
	client();
	return 0;
}

运行结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值