C语言和设计模式-观察者模式

1.卧底是谁派来的

  1. “知己知彼,百战不殆;不知彼知己,一胜一负;不知己不知彼,每战必殆”
  2. 那么怎样才能做到知彼呢,这个在世界都比较常见,就是安插间谍
  3. 李斯和韩非子都是荀子的弟子,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插间谍到各个国家,韩非子这么重量级人物,当然身边少不了间谍,韩非子做的李斯都了如指掌。

1.1场景分析

  1. 观察者:李斯
  2. 被观察者:韩非子(生活起居)
  3. 执行者:间谍

根据这些信息,我们先使用常规的设计思路实现,然后一步一步优化;从而揭露观察者模式的本质。

1.1.1类图—两个重要人物

1.1.2类图--增加监听

1.1.3分析

这是真实世界的翻版,安排了一个间谍,观察韩非子的生活起居,并上报给李斯,然后李斯在触发update事件,其中Spy是间谍角色,专门用来监听韩非子,然后报告给李斯

1.1.4被观察者接口(韩非子)

/*被观察者*/
//被观察者接口
typedef struct _IHanFeiZi
{
	//吃早饭
	void (*haveBreakfast)();
	//娱乐
	void (*haveFun)();
}IHanFeiZi;

1.1.5具体被观察者实现(韩非子)

//具体被观察者
int isHaveBreakfast = 0;
int isHaveFun = 0;

struct _IHanFeiZi HanFeizi
{
	.haveBreakfast = HaveBreakfast,
	.haveFun = HaveFun,
};

void HaveBreakfast()
{
	printf("韩非子:开始吃饭了...\n");
	isHaveBreakfast = 1;
}

void HaveFun()
{
	printf("韩非子:开始娱乐了...\n");
	isHaveFun = 1;
}

//get/set方法
int getHaveBreakfast()
{
	return isHaveBreakfast;
}

void setHaveBreakfast(int isHaveBreakfast)
{
	isHaveBreakfast = isHaveBreakfast;
}

int getHaveFun()
{
	return isHaveFun;
}

void setHaveFun(int isHaveFun)
{
	isHaveFun = isHaveFun;
}

1.1.6观察者接口和实现(李斯)

/*观察者*/
//观察者接口
typedef struct _ILiSi
{
	//发现有动静,要做相应的行动
	void (*update)(char *str);
}ILiSi;

//观察者实现
struct _ILisi Lisi
{
	.update = Update,
};

void Update(char *str)
{
	printf("李斯:报告秦老板,韩非子有活动了--->:%s\n",str);
}

1.1.7间谍线程

/*间谍*/
void SpyThread()
{
	while(1)
	{
		if(1 == getHaveBreakfast())
		{
			Lisi.update("韩非子在吃饭...");
			setHaveBreakfast(0);
		}
		else if(1 == getHavefun())
		{
			Lisi.update("韩非子在娱乐...");
			setHaveFun(0);
		}
		sleep(1);
	}
}

1.1.8场景

//场景
void client()
{
	pthreadSpawn(NULL, 50, 32 * 1024, SpyThread, 0);
	
	sleep(100);
	HanFeizi.haveBreakfast();
	sleep(100);
	HanFeizi.haveFun();
}

1.1.9优化

  1. 当执行这段代码的时候,有没有看到cpu在飙升,【还好其中有sleep】,为什么呢,因为其中写了死循环,我们写的很多监听报警都是这样设计的,不觉得low么
  2. 可以试一试聚合【把李斯聚集在韩非子身上,这在现实生活中比较难以接受,但在程序里无所不能~~】,立马动手

1.2.1聚合方式优化类图

1.2.2聚合方式的被观察者(韩非子)

/*被观察者*/
//被观察者接口
typedef struct _IHanFeiZi
{
	//吃早饭
	void (*haveBreakfast)();
	//娱乐
	void (*haveFun)();
}IHanFeiZi;


//具体被观察者
struct _IHanFeiZi HanFeizi
{
	.haveBreakfast = HaveBreakfast,
	.haveFun = HaveFun,
};

void HaveBreakfast()
{
	printf("韩非子:开始吃饭了...\n");
	Lisi.update("韩非子在吃饭...");
}

void HaveFun()
{
	printf("韩非子:开始娱乐了...\n");
	Lisi.update("韩非子在娱乐...");
}

1.2.3聚合方式的观察者(李斯)

/*观察者*/
//观察者接口
typedef struct _ILiSi
{
	//发现有动静,要做相应的行动
	void (*update)(char *str);
}ILiSi;

//观察者实现
struct _ILisi Lisi
{
	.update = Update,
};

void Update(char *str)
{
	printf("李斯:报告秦老板,韩非子有活动了--->:%s\n",str);
}

1.2.4聚合方式的场景

//场景
void client()
{
	HanFeizi.haveBreakfast();
	HanFeizi.haveFun();
}

1.2.5分析

  1. 在战国争雄的时候,韩非子这么有名望,有实力的人,就只有秦国关心他吗?想想也不能啊,肯定有好多的间谍在监视着…
  2. 直接将LiSi加入到HanFeiZi里面,只有李斯能观察韩非子,这是不对的,如果还有别人观察韩非子呢,需要修改实现代码,不符合开闭原则;

我们继续优化,让代码符合开闭原则。

1.3.1观察者类图优化

  1. 解释:
    1. Observable:实现该接口的是被观察者
    2. Observer:实现该接口的是观察者

1.3.2观察者接口

/*观察者*/
//观察者接口
typedef struct _Observer
{
	//发现有动静,要做相应的行动
	void (*update)(char *str);
}Observer;

1.3.3观察者类实现

//观察者李斯实现
struct _Observer Lisi
{
	.update = Update,
};

void Update(char *str)
{
	printf("李斯:报告秦老板,韩非子有活动了--->:%s\n",str);
}
//观察者王斯实现
struct _Observer Wangsi
{
	.update = sad,
};

void sad(char *str)
{
	printf("王斯:悲伤--->:%s\n",str);
}
//观察者刘斯实现
struct _Observer LiuSi
{
	.update = happy,
};

void happy(char *str)
{
	printf("刘斯:我快乐啊--->:%s\n",str);
}

1.3.4被观察者接口

/*被观察者*/
//被观察者接口
typedef struct _Observable
{
	//增加一个观察者
	void (*addObserver)(Observer *pObserver);
	//删除一个观察者
	void (*delObserver)(Observer *pObserver);
	//通知观察者
	void (*notifyObserver)(char *str);
}Observable;

typedef struct _IHanFeiZi
{
	//吃早饭
	void (*haveBreakfast)();
	//娱乐
	void (*haveFun)();
}IHanFeiZi;

1.3.5被观察者实现

typedef struct _HanFeiZi
{
	Observable 	obs;
	IHanFeiZi 	ihanfeizi;
}HanFeiZi;

#define MAX_BINDING_NUMBER 3
observer* pObserverList[MAX_BINDING_NUMBER];
int number = 0;

//具体被观察者
struct _HanFeiZi HanFeizi
{
	.obs.addObserver = AddObserver;
	.obs.delObserver = DelObserver;
	.obs.notifyObserver = NotifyObserver;
	.ihanfeizi.haveBreakfast = HaveBreakfast,
	.ihanfeizi.haveFun = HaveFun,
};

1.3.6被观察者实现函数

void AddObserver(Observer *pObserver)
{
	int i = 0;
	if(NULL == pObserver)
		return ;
	
	for(i < MAX_BINDING_NUMBER)
	{
		if(NULL == pObserverList[i])
		{
			pObserverList[i] = pObserver;
			number++;
		}
	}
}

void DelObserver(Observer *pObserver)
{
	int i = 0;
	if(NULL == pObserver)
		return ;
		
	for(i < MAX_BINDING_NUMBER)
	{
		if(pObserver == pObserverList[i])
		{
			pObserverList[i] = NULL;
			number--;
		}
	}
}

void NotifyObserver(char *str)
{
	int i = 0;
	if(NULL == str)
		return ;
	
	for(i < MAX_BINDING_NUMBER)
	{
		if(pObserverList[i])
		{
			pObserverList[i].update(str);
		}
	}
}

void HaveBreakfast()
{
	printf("韩非子:开始吃饭了...\n");
	NotifyObserver("韩非子在吃饭...");
}

void HaveFun()
{
	printf("韩非子:开始娱乐了...\n");
	NotifyObserver("韩非子在娱乐...");
}

1.3.7场景

//场景
void client()
{
	AddObserver(&Lisi);
	AddObserver(&Wangsi);
	AddObserver(&LiuSi);
	
	HanFeizi.haveBreakfast();
	HanFeizi.haveFun();
}

从上面的类图设计一步一步优化,最终的到的观察者模式,符合开闭原则,也摆脱了循环监听的这种低效的简陋设计;是不是有种柳暗花明的感觉~~

接下来我们在系统的分析一下观察者模式

2.观察者模式

观察者模式也叫做发布订阅模式(Pulish/subscribe)

Observer Pattern定义:

Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.(定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新)

2.1观察者模式通用类图

  • Subject:被观察者,可以增加观察者、删除观察者、通知观察者
  • Observer:观察者。当接收到被观察者的动作后,自己做出相应的反应
  • ConcreteSubject:具体被观察者--定义自己的业务逻辑
  • ConcreteObserver:具体观察者--定义接收消息后自己的处理逻辑

2.2观察者模式通用代码实现:

2.2.1被观察者:

/*被观察者*/
typedef struct _Subject
{
	observer* pObserverList[MAX_BINDING_NUMBER];
	int number = 0;
	
	//增加一个观察者
	void (*addObserver)(Subject *pSubject,Observer *pObserver);
	//删除一个观察者
	void (*delObserver)(Subject *pSubject,Observer *pObserver);
	//通知观察者
	void (*notifyObserver)(Subject *pSubject,char *str);
}Subject;

2.2.2具体被观察者

//具体被观察者
typedef struct _ConcreteSubject
{
	Subject subject;
	
	//具体的业务
	void (*doSomething)(char *str);
}ConcreteSubject;

//实例化具体被观察者
struct _ConcreteSubject ConcreteSubjectEXP
{
	.subject.pObserverList = NULL,
	.subject.addObserver = AddObserver,
	.subject.delObserver = DelObserver,
	.subject.notifyObserver = NotifyObserver,
	.doSomething = DoSomething,
}

2.2.3具体被观察者接口实现

void AddObserver(Subject *pSubject, Observer *pObserver)
{
	int i = 0;
	if(NULL == pSubject || NULL == pObserver)
		return ;
	
	for(i < MAX_BINDING_NUMBER)
	{
		if(NULL == pObserverList[i])
		{
			pSubject->pObserverList[i] = pObserver;
			pSubject->number++;
		}
	}
}

void DelObserver(Subject *pSubject, Observer *pObserver)
{
	int i = 0;
	if(NULL == pSubject || NULL == pObserver)
		return ;
		
	for(i < MAX_BINDING_NUMBER)
	{
		if(pObserver == pObserverList[i])
		{
			pSubject->pObserverList[i] = NULL;
			pSubject->number--;
		}
	}
}

void NotifyObserver(Subject *pSubject, char *str)
{
	int i = 0;
	if(NULL == pSubject || NULL == pObserver)
		return ;
	
	for(i < MAX_BINDING_NUMBER)
	{
		if(pObserverList[i])
		{
			pSubject->pObserverList[i].update(str);
		}
	}
}

void DoSomething(char *str)
{
	ConcreteSubjectEXP.subject.notifyObserver(str);
}

2.2.4观察者

/*观察者*/
//观察者接口
typedef struct _Observer
{
	//发现有动静,要做相应的行动
	void (*update)(char *str);
}Observer;

//实例化具体观察者
struct _Observer ConcreteObserver
{
	.update = Update,
};

void Update(char *str)
{
	printf("接收到消息:%s\n",str);
}

2.2.5场景

/*场景*/
void client()
{
	ConcreteSubjectEXP.subject.addObserver(&ConcreteObserver);
	
	ConcreteSubjectEXP.DoSomething("干饭");
}

2.3观察者模式优点

  1. 观察者和被观察者之间是抽象耦合【容易扩展】
  2. 建立一套触发机制【有事件的触发--有响应者做出相应的响应动作】

2.3观察者模式缺点

  1. 开发效率--一个被观察者,多个观察者,开发调试会比较复杂
  2. 运行效率--一个卡,后面的卡,建议异步
  3. 多级触发--一个一个发送

2.4观察者模式的使用场景

  1. 关联行为的场景,注意:关联行为是可拆分的,而不是“组合” 关系
  2. 事件多级触发场景
  3. 跨系统的消息交换场景,如消息队列的处理机制(  RocketMQ、Kafka 都是直接基于发布-订阅模型)

参考:设计模式之禅--观察者模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值