首页计算机计算机软件设计模式 03 第二部分 访问者模式.pptx

03 第二部分 访问者模式.pptx

页数:30页文件大小:371.7 KB来源: 设计模式ppt课件

本课件接续行为型设计模式,先从类型判断的困境推出访问者模式的双分派实现,再讲模板方法、职责链与命令模式的意图与代码,最后按行为差异、通知访问与交互复杂三类作小结。

访问者模式引入页,说明在树与链表等结构中操作异构元素时如何去掉类型判断
访问者模式示例,Cat与Dog继承Animal后各自拥有Sleep与Eat等不同操作
类型判断问题分析,if-else膨胀、违背里氏替换原则以及新增操作的困难
查看本课件的文字版(共 30 页,供检索与复制)

第1页:2.5 访问者模式

2.5访问者模式(Visitor Pattern)

  • 意图:针对一个结构中(如树、链表、集合等)的各元素进行操作时,元素的类型可能不尽相同。1)如何在操作时,去掉类型的检测和判断。2)如何允许添加新的操作?

第2页:访问者模式示例:Animal

2.5.1例子class Animal {};class Cat:public Animal{public:virtual void Sleep( );};class Dog:public Animal{public:virtual void Eat();virtual void Run();};void main() {Vector<Animal * >*animals = newVector<Animal *>;animals->Add(new Cat);animals->Add(new Dog);Animal * an;for each(an in animals) {if (dynamic_cast<Cat *> (an)!=0) {((Cat*)an)->Sleep( );} else if (dynamic_cast<Dog *>(an)!=0) {((Dog*)an)->Eat();}}

第3页:类型判断带来的问题

问题:

  • 1) if-else多了不易处理
  • 2)类型的检查(违背里氏替换原则)
  • 3) Dog增加一个操作Run,怎么办?
  • 4)增加一个接口Jump,怎么办?
  • 针对问题1)和2)修改Animal及子类

第4页:修改Animal及子类

修改Animal及子类classAnimal{public:virtual void Do();};class Cat:public Animal{public:virtual void Do( ) {Sleep();}virtualvoid Sleep();};class Dog:public Animal{public:virtual void Do(){ Eat(); }virtual void Eat();};class Animal {};class Cat:public Animal{public:virtual void Sleep( );};class Dog:public Animal{public:virtual void Eat();virtual void Run();};

第5页:统一调用与疑问

for each(an in animals) {if (dynamic_cast<Cat *> (an)!=0) {((Cat*)an)->Sleep( );} else if (dynamic_cast<Dog *> (an)!=0) {((Dog*)an)->Eat();}}for each(an in animals) {an->Do( );}

  • 可以。
  • 但合理吗?Sleep,Eat是一个行为?!
  • 若再增加一个Run操作呢?
  • for each的操作改变了呢?

第6页:分离稳定与变化部分

分离稳定与变化部分

  • 稳定:结构(Animal,Cat,Dog)
变化:操作(Sleep,Eat,Run,…)结构部分(不变,这里简写)class Animal {};class Cat:public Animal{};class Dog:public Animal{};操作部分:(3个不同实现)SleepForCatEatForDogRunForDog如何分组?按Cat、Dog类型分?还是按功能分?

第7页:按猫狗类型对操作分组

按猫狗的类型对操作分组classOperate {public:virtualvoid Sleep();virtualvoid Eat();virtualvoid Run();};class CatOperate:publicOperate {public:virtualvoid Sleep(){}virtualvoid Eat(){}virtualvoid Run(){}};classDogOperate:publicOperate {public:virtualvoid Sleep(){}virtualvoid Eat(){ }virtualvoid Run(){ /* Run() ;* / }};则main中的改为:for each(an in animals) {//创建具体Operate,//需要先确定an是Cat?Dog?operate->Sleep();operate->Eat();}这样不行。还得先确定an的具体类型,才能确定执行的Sleep是哪个类的。

第8页:按功能对操作分组(一)

按功能对操作分组1)class Visitor {public:virtualvoidVisitCat(Cat *){ }virtualvoidVisitDog(Dog *){ }};class SleepVisitor:publicVisitor {public:virtualvoidVisitCat(Cat* cat){ cat->Sleep();}};class EatVisitor:publicVisitor {public:virtualvoidVisitDog(Dog * dog){ dog->Eat();}};class RunVisitor:publicVisitor {public:virtualvoidVisitDog(Dog * dog){ dog->Run();}};

第9页:按功能对操作分组(二)

for each(an in animals) {if (dynamic_cast<Cat *> (an)!=0) {((Cat*)an)->Sleep( );} else if (dynamic_cast<Dog *> (an)!=0) {((Dog*)an)->Eat( );}}按功能对操作分组2)SleepVisitor * v1= new SleepVisitor;EatVisitor * v2= new EatVisitor;for each(an in animals) {DoSome(v1,an);DoSome (v2,an ) ;或v1->VisitCat(an);//????v2->VisitDog(an);//?????//还不知道an是Cat,还是Dog呢!!}

第10页:在结构中增加Accept方法

class Animal {public:virtual void Accept(Visitor * v);//....};class Cat:public Animal {public:virtual void Accept(Visitor * v){v->VisitCat(this); }//....};class Dog:public Animal {public:virtual void Accept(Visitor * v){v->VisitDog(this); }//....};按功能对操作分组3)修改Animal,添加Accept()SleepVisitor * v1= newSleepVisitor;EatVisitor * v2= new EatVisitor;for each(an in animals) {an->Accept(v1);an->Accept(v2);}for each(an in animals) {if (dynamic_cast<Cat *> (an)!=0){((Cat*)an)->Sleep( );} else if (dynamic_cast<Dog *>(an)!=0) {((Dog*)an)->Eat( );}}

第11页:访问者模式适用性

2.5.2访问者模式适用性

  • 对象包含很多子对象或元素,但各元素有不同的接口,但在操作这些元素时,希望做一些涉及或依赖于具体元素类型的操作。
  • 对象结构很少改变,但常需要在此结构上定义新操作。

第12页:访问者模式使用方法

2.5.3访问者模式使用方法1.分离结构和操作。2.将操作按功能分类(而不是按结构分类)3.按功能定义Visitor类及子类4.在结构Element中添加Accept。5.对Element对象,执行操作。

第13页:按功能定义Visitor类及子类

按功能定义Visitor类及子类classFunc1Visitor:publicVisitor {public:virutal void VisitElementA(ElementA * a){a->doFunc1( );}virutal void VisitElementB(ElementB * b ){b->doFunc1( );}virutal void VisitElementC(ElementC * c){c->doFunc1( );}};classFunc2Visitor:publicVisitor {public:virutal void VisitElementA(ElementA * a){a->doFunc2( );}virutal void VisitElementB(ElementB * b){b->doFunc2( );}virutal void VisitElementC(ElementC * c){c->doFunc2( );}};publicVisitor{public:virutal voidVisitElementA(ElementA * a)=0;virutal voidVisitElementB(ElementB * b )=0;virutal voidVisitElementC(ElementC * c)=0;};

第14页:在Element中添加Accept

classBaseElement {public:virtual void Accept(Visitor * v) = 0;//…..};classElementA:public BaseElement {public:virtual void Accept(Visitor * v){v->VisitElementA(this); }//.....};classElementB:public BaseElement {public:virtual void Accept(Visitor * v){v->VisitElementB(this); }//.....};在结构Element中添加Accept()

第15页:访问者模式的执行方式

需执行操作Func1时,可Visitor * v= new Func1Visitor;elem->Accept(v);

  • 需执行操作Func2时,可Visitor * v= new Func2Visitor;elem->Accept(v);访问结构中的元素elem时

第16页:访问者模式结构

2.5.4访问者模式结构

第17页:2.6 模板方法模式

2.6模板方法模式(Template Method Pattern)

  • 意图:不改变操作的主体结构或过程,将其中的特定步骤或过程延迟到子类中实现。

第18页:模板方法模式示例

2.6.1例子class ProcessFile{public:void Process() {OpenFile();DoFile();CloseFile();}protected:virtual void DoFile() { }virtual void OpenFile() { }virtual void CloseFile() {}};class MyProcessFile:publicProcessFileprotected:virtual void OpenFile(){//......}virtual void DoFile() {//......}};

第19页:模板方法模式适用

2.6.2模板方法模式适用

  • 基本过程是稳定的
  • 特定的步骤易变或有待子类确定。
  • 若多个步骤易变,可能导致子类数量的快速增长
  • 可以和策略模式、状态模式等结合使用

第20页:2.7 职责链模式

2.7职责链模式(Chain of Responsibility)

  • 意图:
  • 给多个对象处理请求的机会
  • 解耦发送者和接收者间

第21页:职责链模式示例

2.7.1例子class HelpHandler {public:HelpHandler(HelpHandler * next): handler(next){ }virtual void HandleHelp(){ handler->HandleHelp(); }protected:HelpHandler *handler;};class Widget:public HewlpHandler {public:Widget(HelpHandler * next):HelpHandler(next) {}virtual void HandleHelp( ) {if (canHandle) {//Handle Code} else {handler->HandleHelp();}}};class Button:publicWidget{public:Button(HelpHandler * next):Widget(next) {}virtual void HandleHelp( ) {if (canHandle) {//Handle Code} else if (handler) {handler->HandleHelp();}};void main() {Button * btn = new Button(NULL);Window * win = new Window(btn);win->HandleHelp();delete win;delete btn;}

第22页:职责链模式结构

2.7.2责任链结构

第23页:2.8 命令模式

2.8命令模式(Command Pattern)

  • 意图:
  • 将一个请求(或动作、命令等)封装,这样的请求就可以带参数进行实例化了。因此,同一个请求,不同的参数,可以是不同的实现结果。
  • 请求可能增加
  • 请求可以是请求队列
  • 请求可以被记录、撤销等。

第24页:命令模式示例:Requester

2.8.1例class Receiver {};class ReceiverA:public Receiver {};class ReceiverB:public Receiver {};class ReceiverC:public Receiver {};class ReceiverD:public ReceiverC {};class Requester{public:virtual void DoAction1(ReceiverA * a);virtualvoidDoAction1(ReceiverB * b);virtualvoidDoAction2(ReceiverD * d);};问题:1)增加新的ReceiverA2,怎么办?2)同时增加新的RecieverD2,又怎么办?3)增加一个新的Action,它是由DoAction1(ReceiverA * a)和DoAction1(ReceiverB * b)顺序组成的,又怎么办?4)需要对每个Action,都做日志记录,怎么办?5)需要对每个Action,都可以做撤销操作,怎么办?

第25页:封装DoAction与参数变化

将Requester的DoAction及参数变化单独封装起来:class Requester{public:virtual void DoAction(Command * comd){comd->Excute( );}};

第26页:具体命令CommandAction

class CommandAction2:public Command{public:CommandAction2(ReceiverC *rec):receiver(rec) {}virtual void Excute(){receiver->DoSomething2( ); }private:ReceiverC*receiver;};由Command区分Action及Receiver:class Command {public:virtual void Excute() = 0;};class CommandAction1:public Command{public:CommandAction1(Reciveiver * rec):receiver(rec){ }virtual void Excute(){receiver->DoSomething1(); }private:Receiver*receiver;};

第27页:宏命令与命令队列

队列(例如由两个Command组成的请求队列)class MacroCommod:public Command{public:MacroCommand(Command* comand1,Command* command2):cmd1(command1),cmd2(command2)virtual void Excute( ){cmd1->Excute();cmd2->Excute()}private:Command* cmd1;Command* cmd2;};

第28页:命令模式优缺点

2.8.3命令模式优缺点好处:

  • 容易构造一个命令队列
  • 便于以日志形式记录命令的执行情况
  • 保留并存储命令执行中的各种信息,可以实现命令的撤销和重做
  • 便于增加新的命令缺点:可能会有过多的具体命令类存在

第29页:命令模式结构

2.8.4命令模式结构

第30页:行为型模式小结

2.9小结

  • 基于各种原因导致行为差异
  • 策略模式
  • 状态模式
  • 模板方法模式
  • 命令模式
  • 通知/访问多个对象
  • 责任链模式
  • 观察者模式
  • 访问者模式
  • 交互复杂
  • 中介模式
顶部目录