观察者(Observer)

观察者模式属于设计模式::行为模式的一种,特别的,属于行为对象模式

行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述他们之间的通信模式(协作)。

行为模式分为两种:

  • 行为类模式:使用继承机制在类间分配行为。
  • 行为对象模式:使用对象复合而不是继承。

一言以蔽之

一对多的依赖关系,一个改变,所有依赖他的对象(即观察者)都得到更新。

  • 目的:通知(抽象的通知机制)

  • 举例:更新进度条,需要将进度值传出;MVC架构:Model改变,所有的View都会得到通知。

举例1-进度条

伪代码:

class IProgress{
public:
	virtual void DoProgress(float value) = 0;
	virtual ~IProgress(){}
};

// 可以记录观察者、移除观察者、通知观察者(也有订阅的意味)
class FileSplitter
{
	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.读取大文件

		//2.分批次向小文件中写入
		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){
        for(auto observer : m_iprogressList)
            observer->DoProgress(value);
    }
};

// MainForm为观察者
class MainForm : public Form, public IProgress
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;
    
public:
	void Button1_Click(){
		string filePath = txtFilePath->getText();
		int number = atoi(txtFileNumber->getText().c_str());

		ConsoleNotifier cn;
		FileSplitter splitter(filePath, number);

		splitter.addIProgress(this); 	// 订阅通知
		splitter.addIProgress(&cn) 	// 订阅通知

		splitter.split();
		splitter.removeIProgress(this);	 // 取消订阅
	}

    // 实现接口
	void DoProgress(float value) override {
		progressBar->setValue(value);
	}
};

// 另一个观察者
class ConsoleNotifier : public IProgress {
public:
	virtual void DoProgress(float value){
		cout << ".";
	}
};

举例2-MVC

  • 模型(Model) 用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。Model 有对数据直接访问的权力,例如对数据库的访问。Model不依赖View和Controller,也就是说, Model 不关心它会被如何显示或是如何被操作。但是 Model 中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此 Model 的 View 必须事先在此 Model 上注册,从而,View 可以了解在数据 Model 上发生的改变。
  • **视图(View)**能够实现数据有目的的显示(理论上而已,不是必需的)。在 View 中一般没有程序上的逻辑。为了实现 View 上的刷新功能,View 需要访问它监视的数据模型Model,因此应该事先在被它监视的数据那里注册。(View是Model的观察者)
  • **控制器(Controller)**起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”(Event)包括用户的行为和数据 Model 上的改变(比如UI就是事件驱动的)。

注意:MVC是一种设计理念,不是必须的,但是使用了会让软件的架构看起来更好,解耦合。

Controller manipulates Model:控制器Controller操作数据

Model updates View:观察者View需要在Model中注册,Model就可以通知View啦

View saw by Users, Users use Controller:沟通View与Controller之间的桥梁就是User用户啦,用户看View,想修改时,使用控制器Controller来操控Model。

模板:

// 可以订阅、取消订阅
Attach(Observer* o){  // addObserver(IProcess* iptr);
    Observers.append(o);
}
Detach(Observer* o){  // removeObserver();
    Observers.remove(o);
}

// 通知
Notify(){
    for(var o : Observers)
    	o->Update();    // Update()可以是观察者的方法
}

相关模式

  1. 中介者(Mediator)
  2. 单例模式(Singleton)