1.卧底是谁派来的
- “知己知彼,百战不殆;不知彼知己,一胜一负;不知己不知彼,每战必殆”
- 那么怎样才能做到知彼呢,这个在世界都比较常见,就是安插间谍
- 李斯和韩非子都是荀子的弟子,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插间谍到各个国家,韩非子这么重量级人物,当然身边少不了间谍,韩非子做的李斯都了如指掌。
- 观察者:李斯
- 被观察者:韩非子(生活起居)
- 执行者:间谍
根据这些信息,我们先使用常规的设计思路实现,然后一步一步优化;从而揭露观察者模式的本质。
这是真实世界的翻版,安排了一个间谍,观察韩非子的生活起居,并上报给李斯,然后李斯在触发update事件,其中Spy是间谍角色,专门用来监听韩非子,然后报告给李斯
/*被观察者*/
//被观察者接口
typedef struct _IHanFeiZi
{
//吃早饭
void (*haveBreakfast)();
//娱乐
void (*haveFun)();
}IHanFeiZi;
//具体被观察者
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;
}
/*观察者*/
//观察者接口
typedef struct _ILiSi
{
//发现有动静,要做相应的行动
void (*update)(char *str);
}ILiSi;
//观察者实现
struct _ILisi Lisi
{
.update = Update,
};
void Update(char *str)
{
printf("李斯:报告秦老板,韩非子有活动了--->:%s\n",str);
}
/*间谍*/
void SpyThread()
{
while(1)
{
if(1 == getHaveBreakfast())
{
Lisi.update("韩非子在吃饭...");
setHaveBreakfast(0);
}
else if(1 == getHavefun())
{
Lisi.update("韩非子在娱乐...");
setHaveFun(0);
}
sleep(1);
}
}
//场景
void client()
{
pthreadSpawn(NULL, 50, 32 * 1024, SpyThread, 0);
sleep(100);
HanFeizi.haveBreakfast();
sleep(100);
HanFeizi.haveFun();
}
- 当执行这段代码的时候,有没有看到cpu在飙升,【还好其中有sleep】,为什么呢,因为其中写了死循环,我们写的很多监听报警都是这样设计的,不觉得low么。
- 可以试一试聚合【把李斯聚集在韩非子身上,这在现实生活中比较难以接受,但在程序里无所不能~~】,立马动手
/*被观察者*/
//被观察者接口
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("韩非子在娱乐...");
}
/*观察者*/
//观察者接口
typedef struct _ILiSi
{
//发现有动静,要做相应的行动
void (*update)(char *str);
}ILiSi;
//观察者实现
struct _ILisi Lisi
{
.update = Update,
};
void Update(char *str)
{
printf("李斯:报告秦老板,韩非子有活动了--->:%s\n",str);
}
//场景
void client()
{
HanFeizi.haveBreakfast();
HanFeizi.haveFun();
}
- 在战国争雄的时候,韩非子这么有名望,有实力的人,就只有秦国关心他吗?想想也不能啊,肯定有好多的间谍在监视着…
- 直接将LiSi加入到HanFeiZi里面,只有李斯能观察韩非子,这是不对的,如果还有别人观察韩非子呢,需要修改实现代码,不符合开闭原则;
我们继续优化,让代码符合开闭原则。
- 解释:
- Observable:实现该接口的是被观察者
- Observer:实现该接口的是观察者
/*观察者*/
//观察者接口
typedef struct _Observer
{
//发现有动静,要做相应的行动
void (*update)(char *str);
}Observer;
//观察者李斯实现
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);
}
/*被观察者*/
//被观察者接口
typedef struct _Observable
{
//增加一个观察者
void (*addObserver)(Observer *pObserver);
//删除一个观察者
void (*delObserver)(Observer *pObserver);
//通知观察者
void (*notifyObserver)(char *str);
}Observable;
typedef struct _IHanFeiZi
{
//吃早饭
void (*haveBreakfast)();
//娱乐
void (*haveFun)();
}IHanFeiZi;
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,
};
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("韩非子在娱乐...");
}
//场景
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.(定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新)
- Subject:被观察者,可以增加观察者、删除观察者、通知观察者
- Observer:观察者。当接收到被观察者的动作后,自己做出相应的反应
- ConcreteSubject:具体被观察者--定义自己的业务逻辑
- ConcreteObserver:具体观察者--定义接收消息后自己的处理逻辑
/*被观察者*/
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;
//具体被观察者
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,
}
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);
}
/*观察者*/
//观察者接口
typedef struct _Observer
{
//发现有动静,要做相应的行动
void (*update)(char *str);
}Observer;
//实例化具体观察者
struct _Observer ConcreteObserver
{
.update = Update,
};
void Update(char *str)
{
printf("接收到消息:%s\n",str);
}
/*场景*/
void client()
{
ConcreteSubjectEXP.subject.addObserver(&ConcreteObserver);
ConcreteSubjectEXP.DoSomething("干饭");
}
- 观察者和被观察者之间是抽象耦合【容易扩展】
- 建立一套触发机制【有事件的触发--有响应者做出相应的响应动作】
- 开发效率--一个被观察者,多个观察者,开发调试会比较复杂
- 运行效率--一个卡,后面的卡,建议异步
- 多级触发--一个一个发送
- 关联行为的场景,注意:关联行为是可拆分的,而不是“组合” 关系
- 事件多级触发场景
- 跨系统的消息交换场景,如消息队列的处理机制( RocketMQ、Kafka 都是直接基于发布-订阅模型)
参考:设计模式之禅--观察者模式