设计模式:观察者模式

1. 什么是观察者模式?

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖于它的对象都得到通知并被自动更新。又被称为发布—订阅模式。

观察者模式的本质:触发联动。

当修改目标对象的状态的时候,就会触发相应的通知,然后就会循环调用所有注册观察者对象的相应方法;并且这个联动还是动态的,可以通过注册和取消注册来控制观察者;目标对象和观察者对象的解耦,无论观察者发生了怎样的变化,目标对象总是能够正确地联动起来;

目标和观察者之间是典型的一对多的关系。

一个观察者也可以观察多个目标,如果观察者为多个目标定义的通知更新方法都是update方法的话,这样就会很麻烦,因为需要接收多个目标的通知,则就需要在update方法内部进行区分,这个更新的通知来自于哪一个目标,不同的目标就有着不同的操作。

单向依赖

  1. 观察者和目标是单向依赖的,只有观察者依赖于目标,而目标不会依赖于观察者;
  2. 它们之间的联系主动权始终掌握在目标手中,只有目标知道什么时候需要通知观察者,而观察者始终是被动的,被动的等待目标的通知,等待目标传值给它;
  3. 对于目标而言,所有的观察者都是一样的,目标会被一视同仁的对待。当然也可以进行控制,只不过那样就不是原始的观察者模式了。

实现细节

  1. 目标实现对象要维护观察者的注册信息;
  2. 目标实现对象需要维护引起通知的状态,一般是目标自身的状态;
  3. 观察者实现对象要能接收目标的通知,能够接收目标传递的数据,或者主动去获取目标的数据,方便后续处理;
  4. 如果一个观察者观察多个目标,那么在观察者的更新方法里面,需要去判断是来自于哪一个目标的通知。
class Observer
{
public:
	virtual string GetName() = 0;
	virtual void Update(const string&) = 0;
};

class Subject
{
public:
	virtual void addObserver(Observer* observer) = 0;
	virtual void RemoveObserver(Observer* observer) = 0;
	virtual void NotifyObserver(const string&) = 0;
};


class ObserverFactory : public Observer
{
private:
	string _s;
public:
	ObserverFactory(const string &s)
		:_s(s)
	{}

	~ObserverFactory()
	{
		_s.clear();
	}

	virtual string GetName()
	{
		return _s;
	}
	virtual void Update(const string& s)
	{
		cout << _s << "	  收到了数据更新: " << s << endl;
	}
};

class SubjectFactory : public Subject
{
private:
	vector<Observer*> _vo;
public:
	~SubjectFactory()
	{
		_vo.clear();
	}

	virtual void addObserver(Observer* observer)
	{
		auto it = find(_vo.begin(), _vo.end(), observer);
		if (it != _vo.end())
		{
			cout << "该观察者已经存在,无需添加" << endl;
		}
		else
		{
			_vo.push_back(observer);
		}
	}

	virtual void RemoveObserver(Observer* observer)
	{
		auto it = find(_vo.begin(), _vo.end(), observer);
		if (it == _vo.end())
		{
			cout << "该观察者不存在,无需删除" << endl;
		}
		else
		{
			_vo.erase(it);
		}
	}

	virtual void NotifyObserver(const string& s)
	{
		for (auto& e : _vo)
		{
			e->Update(s);
		}
	}
};

int main()
{
	Subject* aim = new SubjectFactory();


	Observer* reader1 = new ObserverFactory("reader1");
	Observer* reader2 = new ObserverFactory("reader2");
	Observer* reader3 = new ObserverFactory("reader3");

	aim->addObserver(reader1);
	aim->addObserver(reader2);
	aim->addObserver(reader3);
	aim->NotifyObserver("该学习了");
	aim->RemoveObserver(reader3);

	aim->NotifyObserver("该学C++了");
	delete reader1;
	delete reader2;
	delete reader3;
	delete aim;
	return 0;
}

2. 推模型 和 拉模型

推模型:目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据,相当于是在广播通信;

拉模型:目标对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体地信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。在这种情况下,会把目标对象自身通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过引用来获取了。

模型比较

  1. 推模型假定目标对象知道观察者需要的数据;而拉模型是目标对象不知道观察者具体需要什么数据,没有办法的情况下,干脆将自身传递给观察者,去观察者去按需取值;
  2. 推模型可能会使得观察者对象难以复用,因为观察者定义的update方法是按需而定的,可能无法兼顾到没有考虑的情况。那么就需要重新实现观察者或者提供新的update方法,而拉模型不会出现这种情况。

3. 观察者模式优缺点

优点

  1. 观察者和目标之间的抽象耦合:目标只知道观察者接口,并不知道具体的观察者的类;
  2. 动态联动:一个操作会引起其它相关的操作;
  3. 广播通信:目标发送通知给观察者是面向所有注册的观察者,所以每次通知的信息就要对所有注册的观察者进行广播;当然也可以限制广播范围;

缺点
4. 可能会引起无谓的操作:由于观察者模式是广播通信,不管观察者需不需要,每个观察者都会调用update方法,如果观察者不需要执行此次操作,那么这次操作就浪费了;也可能引起误操作。

4. 何时选用观察者模式?

  1. 当一个抽象模型中,一个操作会影响另一个状态的变化时,那么就可以选用观察者模式,将两者封装为观察者和目标对象;
  2. 如果更改一个对象,需要同时连带更改其它对象时,并且不知道有多少对象会被连带更改。被更改的作为目标对象,被连带更改的作为观察者对象;
  3. 当一个对象必须通知其它对象,但这个对象和其它被通知的对象是松散耦合的。这个对象就是目标对象,被它通知的对象就是观察者对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值