前言

在学习侯捷老师的有关设计模式的课程(李建忠老师主讲)中,老师对23种设计模式的有自己的划分,如下。所以老师讲解是按照这种顺序讲解。
在这里插入图片描述
组件协作:
在这里插入图片描述

Observer

1.使用场合

  1. 为某些对象建立一种“通知依赖关系”:即一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。
  2. 场景举例:
  • 微信公众号,如果一个用户订阅了某个公众号,那么便会收到公众号发来的消息,那么,公众号就是『被观察者』,而用户就是『观察者』
  • 气象站可以将每天预测到的温度、湿度、气压等以公告的形式发布给各种第三方网站,如果天气数据有更新,要能够实时的通知给第三方,这里的气象局就是『被观察者』,第三方网站就是『观察者』
  • MVC模式中的模型与视图的关系也属于观察与被观察

以上场景举例来源自:链接

2.示例1:文件分割进度显示

假设我们要将一个大文件分割成几个小文件分别存储,并且在分割过程中实时显示分割的进度。这里就可以使用观察者模式,观察者就是进度显示的类,被观察者就是文件分割类。

观察者可以有多个,被观察者改变时需要通知哪些观察者是客户程序决定的。所以被观察者应该有方法删除和增加需要通知的观察者。

#include <iostream>
#include <list>
#include<string>

using namespace std;

//进度显示接口
class IProgress{
public:
	virtual void DisplayProgress(float value) = 0;
	virtual ~IProgress(){}
};

//文件分割类
class FileSplitter
{
private:
	string m_filePath;
	int m_fileNumber;

	list<IProgress*>  m_iprogresslist; // 抽象通知机制,支持多个观察者

public:
	FileSplitter(const string& filePath, int fileNumber) 
		:m_filePath(filePath),m_fileNumber(fileNumber)
	{}

	void split()
	{
		//1.读取大文件
		cout << "读取文件大小....." << endl;

		//2.分批次向小文件中写入
		cout << "拆分文件,写入小文件大小....." << endl;
		for (int i = 0; i < m_fileNumber; i++){
			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			onProgress( progressValue );//发送通知
		}

	}

	//增加观察者
	void addIProgress(IProgress* iprogress)
	{
		m_iprogresslist.push_back(iprogress);
	}
	//删除观察者
	void removeIProgress(IProgress* iprogress)
	{
		m_iprogresslist.remove(iprogress);
	}

protected:
	//通知观察者
	virtual void onProgress(float value)
	{
		list<IProgress*>::iterator itor = m_iprogresslist.begin();

		while (itor != m_iprogresslist.end()){
			(*itor)->DisplayProgress(value); //观察者的"更新状态"的虚方法
			itor++;
		}
	}
};

//百分比进度显示
class PercentageProgress :public IProgress
{
public:
	virtual void DisplayProgress(float value)
	{
		cout << "文件分割了" << value * 100 << "%" << endl;
	}
};

//模拟窗口程序
class MainForm : public IProgress
{
	string filePath;
	int FileNumber;
public:
	void Button1_Click(){//点击图形界面上的分割按钮时的回调函数

		string filePath = "/C::";//模拟文件路径
		int FileNumber = 10;//模拟文件数量

		FileSplitter splitter(filePath, FileNumber);//1.生成文件分割器

		//2.分割进度显示类,需加入被通知的列表中
		PercentageProgress pp;//2.1 一个百分比进度显示类

		splitter.addIProgress(&pp);//2.2百分比进度显示    订阅通知
		splitter.addIProgress(this); //2.3进度条进度形式  订阅通知

		//3.开始分割文件
		splitter.split();//分割文件
	}

	virtual void DisplayProgress(float value){
		cout << "进度条\t完成" << value << endl;
	}
};

int main()
{
	MainForm mf;//生成窗口
	mf.Button1_Click();//模拟窗口点击

	system("pause");
	return 0;
}

当然,你可能会这样做:让文件分割类FileSplitter直接和进度显示类PercentageProgress组合。然后在文件分割时,直接通过PercentageProgress对象调用其方法显示进度。

#include <iostream>
#include <list>


using namespace std;

class PercentageProgress
{
public:
	void DisplayProgress(float value)
	{
		cout << "显示文件分割百分比的方式." << value * 100 << "%" << endl;
	}
};

class FileSplitter
{
private:
	string m_filePath;
	int m_fileNumber;

	PercentageProgress  m_percemtageprogress; //具体的对象

public:
	FileSplitter(const string& filePath, int fileNumber)
		:m_filePath(filePath), m_fileNumber(fileNumber)
	{}
	void split()
	{
		//1.读取大文件
		cout << "读取文件大小....." << endl;

		//2.分批次向小文件中写入
		cout << "拆分文件,写入小文件大小....." << endl;
		for (int i = 0; i < m_fileNumber; i++){
			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;

			m_percemtageprogress.DisplayProgress(progressValue);
		}
	}
};

int main()
{
	FileSplitter f("/d", 6);

	f.split();

	system("pause");
	return 0;
}

按这样做法,违背了依赖倒置原则,即高层(FileSplitter)依赖低层(PercentageProgress),而不是依赖抽象。而且一旦以后要加一个显示方式,比如加一个进度条形式,那么就要修改FileSplitter,这又违反了开闭原则。

3.示例2

有如下的一个应用场景,即同一个页面,开4个窗口,或者像右下角一样,数据一样,但是窗口的表现形式不一样。当我们改变其中一个窗口的某个值时,其他窗口必定要同步变。
在这里插入图片描述

#include <iostream>
#include <vector>

using namespace std;

class Observer
{
public:
	virtual void updata(void) const = 0;
};

class widowObs1 :public Observer
{
public:
	virtual void updata(void) const
	{
		cout << "窗口1更新" << endl;
	}
};

class Subject
{
public:
	//加入需联动对象
	void attach(Observer* ob)
	{
		mem.push_back(ob);
	}

	//改变窗口值
	void set_value(const int& _val)
	{
		value = _val;
		notify();//通知所有的需联动的对象
	}

	//通知
	void notify(void)
	{
		for (vector<Observer*>::iterator it = mem.begin(); it != mem.end(); it++)
			(*it)->updata();
	}

private:
	vector<Observer*> mem;
	int value;
};

int main()
{
	Subject sub;
	sub.attach(new widowObs1);
	sub.set_value(5);

	system("pause");
	return 0;
}

4.GoF给出的模式定义

在这里插入图片描述

5.模式结构

在这里插入图片描述
注: ConcreteSubject是Subject的子类,它实现了在Subject中定义的抽象业务逻辑方法,还可以有自己的方法,如果无须扩展Subject,则ConcreteSubject可以省略,就像文章给出的两个示例一样,均无ConcreteSubject类。

6.要点总结

  • 使用面向对象的抽象,Observer模式使得我们可以独立地改变目
    标与观察者,从而使二者之间的依赖关系达致松耦合。
  • 目标发送通知时,无需指定观察者(多态机制),通知(可以携带通知信息作
    为参数)会自动传播。
  • 观察者自己决定是否需要订阅通知(客户程序),目标对象对此一无所知。
  • Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐