观察者模式(c++)(组建协作型模式)

应用场景

某一天,你的老板希望你在某个项目的文件下载的界面,加一个进度条,方便用户知道现在文件下载到那一步了。于是乎,你写了个文件加载类(FileLoader),一个进度条类(ShowRate)。把文件加载的进度实时地显示给用户。就像这样

class ShowRateA{
public:
	void setRate(float rate){
		mRate = rate;
	}
	void showRate(){
		//showRate
	}
private:
	float mRate;
}
class FileLoaderA{
public:
	FileLoader(ShowRate* showRate)
	:mShowRate(showRate){}
	
	void loadFile(const char *fliePath){
		//假设有总共10个文件
		for(int i = 0;i<10;i++){
			//加载文件
			mShowRate->setRate(i/10.0);
			mShowRate->showRate();
		}
	}
private:
	ShowRate* mShowRate;
}
int main(void){
	ShowRateA* showRate = new ShowRateA();
	FileLoaderA *fileLoader = new FileLoaderA(showRate);
	
	fileLoader->loadFile("一个神奇的文件路径");
	
	delete fileLoader
	delete showRate;
	
	return 0;
}

看起来好像是解决了问题。实际上这时候你确实解决了问题。

but过了几天,你的老板发现,之前的进度展示界面好像做的太难看了,于是找人写了个更好看的界面,于是需要一个新的进度条(ShowRateB),你需要的是把之前的进度条换掉,这时候你只能重新编译main函数,并且重新编译一大堆文件,导致软件出现一大堆莫名其妙的bug,于是你加班到12点,第二天疯狂掉头发,有过了两天,老板发现之前的文件加载类不够高效,于是找人写了个新的文件加载类(FileLoaderB),于是又要重写main函数,然后一大堆bug又来了,又加班,掉发。
最后秃头。
为什么你会秃头呢?因为这样写代码严重违反了依赖倒置原则,极有可能造成莫名其妙的代码错误。(之前有一篇博客专门讲了软件设计6大原则)

使用观察者模式的目的

定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,在主题对象的状态发生变化时,会通知所有的观察者。
隔离变化
在实际的项目中,使用配置文件+合理的设计模式,可以做到只更新需要的一部分,

class Observer{
public:
	virtual void update(float rate) = 0;
	virtual void showRate() = 0;
	virtual ~Observer();
};

class ShowRateA : public Observer{
public:
	virtual void update(float rate){
		mRate = rate;
	}
	virtual void showRate(){
		//showRate
	}
private:
	float mRate;
};

class Subject{
public:
	virtual void loadFile(const char *fliePath) = 0;
	virtual ~Subject();
};

class FileLoaderA : public Subject{
public:
	FileLoaderA (Observer *observer)
	:mObserver(observer){}
	
	virtual void loadFile(const char *fliePath){
		//假设有总共10个文件
		for(int i = 0;i<10;i++){
			//加载文件
			mObserver->update(i/10.0);
			mObserver->showRate();
		}
	}
private:
	Observer *mObserver;
};
int main(void){
	Observer *showRate = new ShowRateA();
	Subject *fileLoader = new FileLoaderA(showRate);
	
	fileLoader->loadFile("一个神奇的文件路径");
	
	delete fileLoader;
	delete showRate;
	
	return 0;
}

这样写的话就算你再写了个新的ShowRateB或者新的FileLoaderB,也能很好的支持,不需要改写各自代码,可以有人会想,这样写也不main函数,实际上一般情况下,决定加载哪一个类的不是main函数里面的那几行代码,实际上很多软件系统都有xml文件,软件通过加载xml文件来决定加载哪一个类。当你需要更新一个类时,只需要编译新的那个类,然后改写xml文件就行了。(这个操作设计到后面要讲的工厂模式,这里可以先听着,后面会讲)

类图

在这里插入图片描述
Subject(目标)
——目标知道它的观察者。可以有任意多个观察者观察同一个目标;
——提供注册和删除观察者对象的接口。

Observer(观察者)
——为那些在目标发生改变时需获得通知的对象定义一个更新接口。

ConcreteSubject(具体目标)
——将有关状态存入各ConcreteObserver对象;
——当它的状态发生改变时,向它的各个观察者发出通知。

ConcreteObserver(具体观察者)
——维护一个指向ConcreteSubject对象的引用;
——存储有关状态,这些状态应与目标的状态保持一致;
——实现Observer的更新接口以使自身状态与目标的状态保持一致。

适用场景

在以下任一情况下都可以使用观察者模式:

  1. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立的改变和复用;
  2. 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变;
  3. 当一个对象必须通知其它对象,而它又不能假定其它对象是谁;也就是说,你不希望这些对象是紧密耦合的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值