设计模式5 行为模式



行为模式,目录:模式方法模式命令模式策略模式观察者模式


模板方法模式:冲咖啡,冲茶水

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;//抽象的制作饮料方法classMakeDrink{public://1把水煮开voidboil(){cout<<"把水煮开"<<endl;}//2冲某物virtualvoidbrew()=0;//3从大杯倒入小杯voidputInCup(){cout<<"把冲泡好的饮料从大杯倒入小杯"<<endl;}//4加一些酌料virtualvoidaddThings()=0;//钩子函数,hookvirtualboolCustomWantAddThings(){returntrue;}//业务的逻辑的统一模板voidmake(){boil();brew();//子类putInCup();if(CustomWantAddThings()==true){addThings();//子类的多态}}};//制作咖啡classMakeCoffee:publicMakeDrink{public:MakeCoffee(boolisAdd){this->isAdd=isAdd;}//2冲某物virtualvoidbrew(){cout<<"冲泡咖啡豆"<<endl;}//4加一些酌料virtualvoidaddThings(){cout<<"添加糖和牛奶"<<endl;}virtualboolCustomWantAddThings(){returnisAdd;}private:boolisAdd;};//冲泡茶叶classMakeTea:publicMakeDrink{public:MakeTea(boolisAdd){this->isAdd=isAdd;}//2冲某物virtualvoidbrew(){cout<<"冲泡茶叶"<<endl;}//4加一些酌料virtualvoidaddThings(){cout<<"添加柠檬或者菊花"<<endl;}virtualboolCustomWantAddThings(){returnisAdd;}private:boolisAdd;};intmain(void){MakeDrink*makeCoffee=newMakeCoffee(true);makeCoffee->make();cout<<"------"<<endl;MakeDrink*makeTea=newMakeTea(false);makeTea->make();return0;}chunli@linux:~$g++main.cpp&&./a.out把水煮开冲泡咖啡豆把冲泡好的饮料从大杯倒入小杯添加糖和牛奶------把水煮开冲泡茶叶把冲泡好的饮料从大杯倒入小杯chunli@linux:~$


模板方法模式中的角色和职责

AbstractClass(抽象类):在抽象类中定义了一系列基本操作

(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一

个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同

时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法

的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽

象类的子类中实现的基本方法,还可以调用其他对象中的方法。

ConcreteClass(具体子类): 它是抽象类的子类,用于实现在父类中声

明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现

的具体基本操作。


模板方法的优缺点

优点:

(1) 在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在

子类实现详细的处理算法时并不会改变算法中步骤的执行次序。

(2) 模板方法模式是一种代码复用技术,它在类库设计中尤为重要,它提取

了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行

为,它鼓励我们恰当使用继承来实现代码复用。

(3) 可实现一种反向控制结构,通过子类覆盖父类的钩子方法来决定某一特

定步骤是否需要执行。

(4) 在模板方法模式中可以通过子类来覆盖父类的基本方法,不同的子类可

以提供基本方法的不同实现,更换和增加新的子类很方便,符合单一职责原则

和开闭原则。

缺点:

需要为每一个基本方法的不同实现提供一个子类,如果父类中可变的基本

方法太多,将会导致类的个数增加,系统更加庞大,设计也更加抽象。


适用场景

(1)具有统一的操作步骤或操作过程;

(2) 具有不同的操作细节;

(3) 存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各


不相同;

在抽象类中统一操作步骤,并规定好接口;让子类实现接口。这样

可以把各个具体的子类和操作步骤解耦合。


命令模式:病人看病直接找医生,耦合度太高.

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classDoctor{public:voidtreatEyes(){cout<<"treateyes"<<endl;}voidtreatNose(){cout<<"treatnose"<<endl;}};intmain(void){Doctor*doctor=newDoctor;doctor->treatEyes();doctor->treatNose();return0;}chunli@linux:~$g++main.cpp&&./a.outtreateyestreatnosechunli@linux:~$




拿着病单来看医生,医生只跟病单打交道

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classDoctor{public:voidtreatEyes(){cout<<"treateyes"<<endl;}voidtreatNose(){cout<<"treatnose"<<endl;}};classCommandEyes{public:CommandEyes(Doctor*doctor){this->doctor=doctor;}~CommandEyes(){if(this->doctor!=NULL){deletethis->doctor;this->doctor=NULL;}}voidtreat(){this->doctor->treatEyes();}private:Doctor*doctor;};classCommandNose{public:CommandNose(Doctor*doctor){this->doctor=doctor;}~CommandNose(){if(this->doctor!=NULL){deletethis->doctor;this->doctor=NULL;}}voidtreat(){this->doctor->treatNose();}private:Doctor*doctor;};intmain(void){//拿着病单来看医生CommandEyes*commandEyes=newCommandEyes(newDoctor);commandEyes->treat();CommandNose*commandNose=newCommandNose(newDoctor);commandNose->treat();return0;}chunli@linux:~$g++main.cpp&&./a.outtreateyestreatnosechunli@linux:~$


将病单抽象出来

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classDoctor{public:voidtreatEyes(){cout<<"treateyes"<<endl;}voidtreatNose(){cout<<"treatnose"<<endl;}};classCommand{public:Command(Doctor*doctor){this->doctor=doctor;}~Command(){if(this->doctor!=NULL){deletethis->doctor;this->doctor=NULL;}}virtualvoidtreat()=0;protected:Doctor*doctor;};classCommandEyes:publicCommand{public:CommandEyes(Doctor*doctor):Command(doctor){}voidtreat(){this->doctor->treatEyes();}};classCommandNose:publicCommand{public:CommandNose(Doctor*doctor):Command(doctor){}voidtreat(){this->doctor->treatNose();}};intmain(void){Command*comd1=newCommandEyes(newDoctor);comd1->treat();Command*comd2=newCommandNose(newDoctor);comd2->treat();return0;}chunli@linux:~$g++main.cpp&&./a.outtreateyestreatnosechunli@linux:~$




让护士接病单,通知病人来看病:

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classDoctor{public:voidtreatEyes(){cout<<"treateyes"<<endl;}voidtreatNose(){cout<<"treatnose"<<endl;}};classCommand{public:Command(Doctor*doctor){this->doctor=doctor;}~Command(){if(this->doctor!=NULL){deletethis->doctor;this->doctor=NULL;}}virtualvoidtreat()=0;protected:Doctor*doctor;};classCommandEyes:publicCommand{public:CommandEyes(Doctor*doctor):Command(doctor){}voidtreat(){this->doctor->treatEyes();}};classCommandNose:publicCommand{public:CommandNose(Doctor*doctor):Command(doctor){}voidtreat(){this->doctor->treatNose();}};classNurse{public:Nurse(Command*cmd){this->cmd=cmd;}~Nurse(){if(this->cmd!=NULL){deletethis->cmd;this->cmd=NULL;}}voidnotify(){this->cmd->treat();}private:Command*cmd;};intmain(void){Nurse*nurse=newNurse(newCommandEyes(newDoctor));nurse->notify();Nurse*nurse2=newNurse(newCommandNose(newDoctor));nurse2->notify();return0;}chunli@linux:~$g++main.cpp&&./a.outtreateyestreatnosechunli@linux:~$



把病单全部给护士长,让护士长下发

chunli@linux:~$catmain.cpp#include<iostream>#include<list>usingnamespacestd;classDoctor{public:voidtreatEyes(){cout<<"treateyes"<<endl;}voidtreatNose(){cout<<"treatnose"<<endl;}};classCommand{public:Command(Doctor*doctor){this->doctor=doctor;}virtual~Command(){if(this->doctor!=NULL){deletethis->doctor;this->doctor=NULL;}}virtualvoidtreat()=0;protected:Doctor*doctor;};classCommandEyes:publicCommand{public:CommandEyes(Doctor*doctor):Command(doctor){}voidtreat(){this->doctor->treatEyes();}};classCommandNose:publicCommand{public:CommandNose(Doctor*doctor):Command(doctor){}voidtreat(){this->doctor->treatNose();}};classNurse{public:Nurse(Command*cmd){this->cmd=cmd;}~Nurse(){if(this->cmd!=NULL){deletethis->cmd;this->cmd=NULL;}}voidnotify(){this->cmd->treat();}private:Command*cmd;};classNurseBoss{public:NurseBoss(){m_list.clear();}~NurseBoss(){m_list.clear();}voidsetCmd(Command*cmd){m_list.push_back(cmd);}voidnotify(){for(list<Command*>::iteratorit=m_list.begin();it!=m_list.end();it++){(*it)->treat();}}private:list<Command*>m_list;};intmain(void){NurseBoss*woman=newNurseBoss;Command*cmd1=newCommandEyes(newDoctor);Command*cmd2=newCommandNose(newDoctor);woman->setCmd(cmd1);woman->setCmd(cmd2);woman->notify();return0;}chunli@linux:~$g++main.cpp-Wall&&./a.outtreateyestreatnosechunli@linux:~$


命令模式,路边吃烤串案例

chunli@linux:~$catmain.cpp#include<iostream>#include<list>usingnamespacestd;classCooker{public:voidmakeChuaner(){cout<<"正在烤串"<<endl;}voidmakeChicken(){cout<<"正在烤鸡翅"<<endl;}};classCommand{public:Command(Cooker*cooker){this->cooker=cooker;}virtual~Command(){if(this->cooker!=NULL){deletethis->cooker;this->cooker=NULL;}}virtualvoidexecute()=0;protected:Cooker*cooker;};classCommandChuaner:publicCommand{public:CommandChuaner(Cooker*cooker):Command(cooker){}virtualvoidexecute(){this->cooker->makeChuaner();}};classCommandChicken:publicCommand{public:CommandChicken(Cooker*cooker):Command(cooker){}virtualvoidexecute(){this->cooker->makeChicken();}};classWaitress{public:Waitress(){this->m_list.clear();}~Waitress(){this->m_list.clear();}voidsetCmd(Command*cmd){this->m_list.push_back(cmd);}voidnotify(){list<Command*>::iteratorit=m_list.begin();for(;it!=m_list.end();it++){(*it)->execute();//在此发生了多态}}private:list<Command*>m_list;};intmain(void){Waitress*mm=newWaitress;//喊一个服务器Command*chuaner=newCommandChuaner(newCooker);//点菜单Command*chicken=newCommandChicken(newCooker);//点菜单mm->setCmd(chuaner);//服务员登记mm->setCmd(chicken);mm->notify();//服务员端菜return0;}chunli@linux:~$g++main.cpp-Wall&&./a.out正在烤串正在烤鸡翅chunli@linux:~$



命令模式中的角色和职责 [图]



Command(抽象命令类): 抽象命令类一般是一个抽象类或接口,在

其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收

者的相关操作。

ConcreteCommand(具体命令类):具体命令类是抽象命令类的子

类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者

对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作

(Action)。

Invoker(调用者): 调用者即请求发送者,它通过命令对象来执行请

求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间

存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体

命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。

Receiver(接收者): 接收者执行与请求相关的操作,它具体实现对请

求的业务处理。



5.2.3 命令模式的优缺点

优点:

(1) 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请

求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,

相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。

(2) 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影

响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚

至客户类代码,满足“开闭原则”的要求。

(3) 可以比较容易地设计一个命令队列或宏命令(组合命令)。

缺点:

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个

对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能

需要提供大量的具体命令类,这将影响命令模式的使用。



5.2.4 适用场景

(1) 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直

接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者

也无须关心何时被调用。

(2) 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令

对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可

能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请

求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来

具体实现。

(3) 系统需要将一组操作组合在一起形成宏命令。




===========================================



策略模式,英雄更换装备战斗场景


chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classAbstractStrategy//抽象的策略{public:virtualvoiduseWeapon()=0;};classKnifeStrategy:publicAbstractStrategy{public:virtualvoiduseWeapon(){cout<<"使用×××"<<endl;}};classAKStrategy:publicAbstractStrategy{public:virtualvoiduseWeapon(){cout<<"使用AK47"<<endl;}};classHero{public:Hero(){strategy=NULL;}voidsetStrategy(AbstractStrategy*strategy){this->strategy=strategy;}voidfight(){cout<<"英雄开始战斗"<<endl;this->strategy->useWeapon();}private:AbstractStrategy*strategy;};intmain(void){AbstractStrategy*knife=newKnifeStrategy;AbstractStrategy*ak47=newAKStrategy;Hero*hero=newHero;cout<<"---------------兵快来了,换AK47------------"<<endl;hero->setStrategy(ak47);hero->fight();cout<<"---------------近距离搏斗,换AK47------------"<<endl;hero->setStrategy(knife);hero->fight();return0;}chunli@linux:~$g++main.cpp-Wall&&./a.out---------------兵快来了,换AK47------------英雄开始战斗使用AK47---------------近距离搏斗,换AK47------------英雄开始战斗使用×××chunli@linux:~$



策略模式,不同时间不同的销售模式

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classAbstractStrategy{public:virtualdoublegetPrice(doubleprice)=0;};classStrategyA:publicAbstractStrategy//8折策略{public:virtualdoublegetPrice(doubleprice){returnprice*0.8;}};classStrategyB:publicAbstractStrategy//满200减100策略{public:virtualdoublegetPrice(doubleprice){if(price>200){price-=100;}returnprice;}};classItem{public:Item(stringname,doubleprice){this->name=name;this->price=price;}doubleSellPrice(){returnthis->strategy->getPrice(this->price);}voidsetStrategy(AbstractStrategy*strategy){this->strategy=strategy;}private:stringname;doubleprice;AbstractStrategy*strategy;};intmain(void){Itemit("nike鞋",201);AbstractStrategy*sa=newStrategyA;AbstractStrategy*sb=newStrategyB;cout<<"上午,全场8折"<<endl;it.setStrategy(sa);cout<<"nike鞋应该卖"<<it.SellPrice()<<endl;cout<<"下午,全场满200减100"<<endl;it.setStrategy(sb);cout<<"nike鞋应该卖"<<it.SellPrice()<<endl;return0;}chunli@linux:~$g++main.cpp-Wall&&./a.out上午,全场8折nike鞋应该卖160.8下午,全场满200减100nike鞋应该卖101chunli@linux:~$


策略模式中的角色和职责[图]


Context(环境类): 环境类是使用算法的角色,它在解决某个问题(即

实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引

用实例,用于定义所采用的策略。

Strategy(抽象策略类): 它为所支持的算法声明了抽象方法,是所有

策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策

略类中声明的方法在运行时调用具体策略类中实现的算法。

ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算

法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一

种具体的算法实现某个业务处理。



5.3.3 策略模式的优缺点

优点:

(1) 策略模式提供了对“开闭原则”的完美支持, 用户可以在不修改原有系

统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。

(2) 使用策略模式可以避免多重条件选择语句。多重条件选择语句不易维护,

它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,

将它们全部硬编码(Hard Coding)在一个庞大的多重条件选择语句中,比直接继

承环境类的办法还要原始和落后。

(3) 策略模式提供了一种算法的复用机制。由于将算法单独提取出来封装在

策略类中,因此不同的环境类可以方便地复用这些策略类。

缺点:

(1) 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意

味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策

略模式只适用于客户端知道所有的算法或行为的情况。

(2) 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系

统要增加一个新的具体策略类。




5.3.4 适用场景

准备一组算法,并将每一个算法封装起来,使得它们可以互换。




=========================================



观察者模式,班长给学生放风,学生抄作业

chunli@linux:~$catmain.cpp#include<iostream>#include<list>usingnamespacestd;//------------------------------------------//抽象层的观察者classListener//学生{public:virtualvoidDoSomething()=0;//该干的virtualvoidDoOtherthing()=0;//不该干的};//抽象的被观察者classNotify//班长{public:virtualvoidaddlistener(Listener*listener)=0;//添加学生virtualvoiddellistener(Listener*listener)=0;//删除学生virtualvoidnotify()=0;//通知学生};//--------------------------------------------classStudent:publicListener//学生{public:Student(stringname,stringbadthing){this->name=name;this->badthing=badthing;}virtualvoidDoSomething(){cout<<"学生"<<name<<"停止"<<badthing<<",改为写作业"<<endl;}virtualvoidDoOtherthing(){cout<<"学生"<<name<<"目前正在"<<badthing<<endl;}private:stringname;stringbadthing;};classMonitor:publicNotify{public:virtualvoidaddlistener(Listener*listener){this->m_list.push_back(listener);}virtualvoiddellistener(Listener*listener){this->m_list.remove(listener);}virtualvoidnotify(){list<Listener*>::iteratorit=m_list.begin();for(;it!=m_list.end();it++){(*it)->DoSomething();}}private:list<Listener*>m_list;};intmain(void){Listener*s1=newStudent("吴用","智取生辰纲");Listener*s2=newStudent("杨志","买刀");Listener*s3=newStudent("武松","醉打蒋门神");Notify*master=newMonitor;master->addlistener(s1);master->addlistener(s2);master->addlistener(s3);cout<<"----老师没有来,大家可以抄作业"<<endl;s1->DoOtherthing();s2->DoOtherthing();s3->DoOtherthing();cout<<"----老师来了,大家该干啥干啥"<<endl;master->notify();return0;}chunli@linux:~$chunli@linux:~$g++main.cpp-Wall&&./a.out----老师没有来,大家可以抄作业学生吴用目前正在智取生辰纲学生杨志目前正在买刀学生武松目前正在醉打蒋门神----老师来了,大家该干啥干啥学生吴用停止智取生辰纲,改为写作业学生杨志停止买刀,改为写作业学生武松停止醉打蒋门神,改为写作业chunli@linux:~$


观察者模式,武林高手对决场景

chunli@linux:~$catmain.cpp#include<iostream>#include<string>#include<list>usingnamespacestd;//前置声明notifierclassNotifier;//抽象的观察者classLinstenner{public://当朋友被揍了我改怎么办virtualvoidonFriendBeFight(Linstenner*one,Linstenner*another/*,Notifier*baixiao*/)=0;//one是打别人的人,another是被揍的virtualvoidfighting(Linstenner*another,Notifier*notifier)=0;virtualstringgetName()=0;virtualstringgetParty()=0;};//抽象的通知者classNotifier{public://添加观察者virtualvoidaddListenner(Linstenner*listenner)=0;//删除观察者virtualvoiddelListenner(Linstenner*listenner)=0;//通知观察者virtualvoidnotify(Linstenner*one,Linstenner*another)=0;};//武林中的人物classHero:publicLinstenner{public:Hero(stringname,stringparty){this->name=name;this->party=party;}//当我发现一个消息之后我该怎么办virtualvoidonFriendBeFight(Linstenner*one,Linstenner*another/*,Notifier*baixiao*/){if(another->getName()!=this->name&&one->getName()!=this->name){//不是当事人//如果不是当事人,需要判断打人的pary和被打的party是不是我自己哥们if(one->getParty()==this->party){//自己人把比人揍了cout<<name<<"发现自己人把别人揍了,笑了,拍手叫好"<<endl;}elseif(another->getParty()==this->party){//自己人被揍了cout<<name<<"发现自己人被别人走了,出手援救"<<endl;//this->fighting(one,baixiao);}}else{//当事人//如果是当事人,什么都不敢}}//揍人的方法virtualvoidfighting(Linstenner*another,Notifier*notifier){cout<<name<<"["<<this->party<<"]"<<"把"<<another->getName()<<"["<<another->getParty()<<"]"<<"给揍了"<<endl;//揍完之后,这个事件应该让百晓生知晓//应该调用百晓生的notify方法notifier->notify(this,another);}stringgetName(){returnthis->name;}stringgetParty(){returnthis->party;}private:stringname;stringparty;};classBaixiao:publicNotifier{public://添加观察者virtualvoidaddListenner(Linstenner*listenner){this->l_list.push_back(listenner);}//删除观察者virtualvoiddelListenner(Linstenner*listenner){this->l_list.remove(listenner);}//通知观察者virtualvoidnotify(Linstenner*one,Linstenner*another){for(list<Linstenner*>::iteratorit=l_list.begin();it!=l_list.end();it++){(*it)->onFriendBeFight(one,another/*,this*/);}}private://拥有所有武林人士的名单list<Linstenner*>l_list;};intmain(void){Linstenner*hong7=newHero("洪七公","丐帮");Linstenner*huangrong=newHero("黄蓉","丐帮");Linstenner*wuyazi=newHero("无崖子","逍遥派");Linstenner*tonglao=newHero("天山童姥","逍遥派");//创建一个百晓生Notifier*baixiao=newBaixiao;//百晓生手机全部的武林人士名单baixiao->addListenner(hong7);baixiao->addListenner(huangrong);baixiao->addListenner(wuyazi);baixiao->addListenner(tonglao);//以上初始化完毕hong7->fighting(wuyazi,baixiao);cout<<"----"<<endl;tonglao->fighting(hong7,baixiao);return0;}chunli@linux:~$g++main.cpp&&./a.out洪七公[丐帮]把无崖子[逍遥派]给揍了黄蓉发现自己人把别人揍了,笑了,拍手叫好天山童姥发现自己人被别人走了,出手援救----天山童姥[逍遥派]把洪七公[丐帮]给揍了黄蓉发现自己人被别人走了,出手援救无崖子发现自己人把别人揍了,笑了,拍手叫好chunli@linux:~$


观察者模式中的角色和职责 [图]



Subject(被观察者或目标,抽象主题):被观察的对象。当需要被观察的状

态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,

删除,通知)一个观察者对象的队列列表。

ConcreteSubject(具体被观察者或目标,具体主题): 被观察者的具体实

现。包含一些基本的属性状态及其他操作。

Observer(观察者):接口或抽象类。当Subject的状态发生变化时,

Observer对象将通过一个callback函数得到通知。

ConcreteObserver(具体观察者):观察者的具体实现。得到通知后将完成

一些具体的业务逻辑处理。



5.4.3 观察者模式的优缺点

优点:

(1) 观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更

新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体

观察者角色。

(2) 观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只

需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观

察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。

(3) 观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送

通知,简化了一对多系统设计的难度。

(4) 观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原

有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新

的观察目标也很方便。

缺点:

(1) 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通

知到会花费很多时间。

(2) 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间

进行循环调用,可能导致系统崩溃。

(3) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生

变化的,而仅仅只是知道观察目标发生了变化。




5.4.4 适用场景

(1) 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两

个方面封装在独立的对象中使它们可以各自独立地改变和复用。

(2) 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道

具体有多少对象将发生改变,也不知道这些对象是谁。

(3) 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的

行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。