
设计模式DesignPattern.ppt
38页第13章 设计模式(Design Pattern )13..2 可重用的面向对象设计模式可重用的面向对象设计模式13.2.1 设计模式概述设计模式概述1. 面向对象设计的任务:面向对象设计的任务: 应用系统设计应用系统设计工具库设计工具库设计框架设计框架设计其中,框架是已形成源代码的可重用软件体系结构它体现了应用程序的模块组成关系框架及框架中的各个模块是形形色色各具特色的设计模式(Design Pattern)描述了软件开发过程中若干重复出现的问题的解决方案,这些方案不是由过程、算法等底层程序构造实体实现,而是由软件系统中类与类之间或不同类的对象之间的共生关系组成设计模式可以帮助软件设计人员学习、重用前人的经验和成果设 计 模 式 的 分 类 整 理 最 早 见 于 Erich Gamma在德国慕尼黑大学的博士论文1995年,Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides合著的《 Design Patterns:Elements of Reusable Object_Oriented Software》系统地整理和描述了23个精选的设计模式(goF模式),为设计模式的学习、研究和推广提供了良好的范例。
ClassNameFunction1()Function2()…DataMenber1DataMenber2…类的表示:子类关系:BaseClassSubclass2.设计模式的描述符号.设计模式的描述符号(1)类和对象的表示 对象及对象链接:aObjectanotherObject((2)连接的表示)连接的表示 例如:由类A创建类B的对象 ClassAClassB对象创建关系:对象创建关系:示例代码:classA::create(){ return new ClassB;}实例引用关系实例引用关系:ClassAClassBrefrenceName(引用名可选)Window::Area(){return aRect→Area();}WindowArea()RectangleArea()widthheightRectangle::Area(){ return width* height;} 引用关系涉及到的两个类的实例之间可以形成一种代理关系,接受请求的对象将操作委托给它的代理者:aRect对象聚合关系:对象聚合关系:objectsClassAClassB如:下图表示类A中聚合了多个类B的对象。
聚合关系可以用对象成员实现,但更经常的是将聚合的成员定义为对象成员指针或引用由于引用关系也是以对象成员指针或引用来实现的,注意从设计意图上区分对象聚合关系和引用关系对理解设计模式是至关重要的聚合是对象的包容关系,容器和容器中的对象具有相同的生命期引用关系又称相识关系,是一种较松散的耦合关系具有引用关系的对象,仅仅是知道被引用者的存在,并不为对方负责, 1.抽象工厂(.抽象工厂(Abstract Factory))模式模式抽象工厂定义一个抽象基类,为创建组合对象提供接口在面向对象系统中,单个对象的创建由构造函数负责一个组合对象的动态创建可由一个创创建建函函数数一次性完成,以保证被创建的组合对象的完备性抽象工厂定义的接口称为创建函数或初始化函数抽象工厂的不同实现类(可称为实现工厂)的对象可以按不同的风格去实现组合对象的具体创建,可以在保证组合对象被完备创建的前提下,简化组合对象的版本替换、升级换代过程工厂方法(工厂方法(Factory Method))是一些动态创建对象的方法是在抽象工厂中声明的一组虚函数,它们负责组合对象中成员对象的创建,其实现代码在实现工厂中定义 抽象工厂模式通常与工厂方法配合使用抽象工厂模式通常与工厂方法配合使用Product1Part11Part12Product2Part21Part22ProductFactoryProductCreate()FactoryMethod1()FactoryMethod2()ConcreatBFactoryMethod1()FactoryMethod2()ConcreatAFactoryMethod1()FactoryMethod2()图13.1 抽象工厂和工厂方法模式结构举例1.创建函数:.创建函数:一个抽象的产品工厂类ProductFactory定义了由两个零件组成的组件产品的创建函数Product* ProductCreate (); 以及创建零件的工厂方法。
抽象的产品工厂的实现工厂ConcreteProduct重定义了创建零件对象的工厂方法创建函数Product* ProductCreate ()调用工厂方法,一次性创建产品返回产品指针2.产品类与零件类.产品类与零件类产品类Product与零件类Part可以是分别定义的类,产品类以零件类为自己的对象成员ProductCreate ()创建一个由2个Part1和1个Part2组合而成的Product组合对象如果将Product定义为抽象基类,且Part1和Part2定义为它的实现类,则可以得到更加复杂的Product组合关系class ProductFactory { //抽象工厂定义Public: Product* ProductCreate ();Virtual Part1*FactoryMethod1(){};Virtual Part2*FactoryMethod2(){};//…};class ConcreteA: public ProductFactory { //实现工厂定义Public: Virtual Part1*FactoryMethod1(){ return new Part1; };Virtual Part2*FactoryMethod2(){ return new Part2; };//…};Product* ProductFactory:: ProductCreate (){ Product* ptr;Part1* p11= FactoryMethod1();Part1* p12= FactoryMethod1();Part1* p21= FactoryMethod2();Ptr=p11;Return ptr;}class Product;class Part1;class Part2;3.用工厂对象参数化参数化组合对象创建过程:工厂对象即实现工厂的实例对象。
修改生产函数ProductCreate()如下:Product* ProductCreate ( ProductFactory* );//创建函数以抽象工厂的指针为形参 若再定义一个抽象工厂的实现类:class ConcreteB: public ProductFactory然后声明:ConcreteA* FactoryA; ConcretetB* FactoryB;则调用:ProductCreate (FactoryA);ProductCreate (FactoryA );将生产A、B两个不同系列的产品很容易实现产品系列的更新换代如:Product和Part分别代表电脑主机和配件,产品更新换代只要定义新的抽象工厂的实现类,并用它来参数化生产函数即可,不必对系统的其它部分作任何改动class Application{static Application * instance;Application ( ){};public:static int count;~Application ( );static Application* init();};Application *Application::instance=NULL;int Application::count=0;Application *Application::init(){ if (count==0) { count=count+1; instance = new Application; cout<<"Single instance has created, OK!!!"< 接访问2.递归组合(.递归组合(Composite))模式:模式: 递归组合模式简称组合模式,通过对象递归组合,形成“对象树”,以表示“整体-部分”关系的层次结构与体现继承性的“类树”结构不同组合模式提供了一种构造结构复杂的大对象的手段 组合模式中含有两种类型的对象:基元对象和组合对象组合模式使组合对象和基元对象具有一致的使用方式PictureDraw()Add(Graphic)Delete(Graphic)GetChild(int)GraphicDraw()Add(Graphic)Delete(Graphic)GetChild(int)RectangleDraw()LineDraw()TextDraw()图13.2 图形编辑器中的递归组合例如在图形编辑器应用程序中,按下图所示组合图形编辑对象,可以按用户意愿最终得到任意大小的图形对象组合文件图图中中Line, Rectangle, Text是是产产生生基基元元对对象象的的类类,,而而Picture是是一一个个组合对象类组合对象类递归组合模式的关键是一个抽象类,它既可以代表递归组合模式的关键是一个抽象类,它既可以代表图元,又可以代表图元的容器。 在上图中这个类就图元,又可以代表图元的容器在上图中这个类就是是Graphic,,它声明一些与特定图形对象相关的操作,它声明一些与特定图形对象相关的操作,例如例如Draw()同时它又声明了所有的组合对象共享同时它又声明了所有的组合对象共享的一些操作,例如一些操作用于访问和管理它的子的一些操作,例如一些操作用于访问和管理它的子部件Graphic的子类Line,Rectangle,Text实现Draw(),以完成各自的绘图功能基元对象不含子部件,它们都不执行与子部件有关的功能Picture类是一个聚合Graphic对象的类它的Draw()操件是通过对它的子部件调用Draw()实现的同时它还定义了一些用于访问和管理它的子部件的其它操作aPictureaLineaPictureaRecttangleaLineaRecttangleaText图13.3 递归组合的对象树递归组合模式的对象一般用工厂方法创建Graphic可以产生如下所示的对象树形组合结构:class Picture;class Graphic {Public: Virtual void Draw()=0;Virtual void Add(Graphic){};Virtual void Delete(Graphic){};Virtual Picture *GetPicture (){return 0;} };class Picture: public Graphic {Public: void Draw();void Add(Graphic);void Remove(Graphic);Picture *GetPicture (){return this;}};class Rectangle: public Graphic {Public: void Draw();};为了识别一个组件是基元还是组合,可按以下示例代码,定义一 个 查 询 函 数 GetPicture(),对其返回的组合安全地执行Add()和 Remove()操作:查询函数GetPicture ()测试代码如下:Picture* aPicture = new Picture;Rectangle* aRectangle = new Rectangle; Graphic* aGraphic;Picture* test;aGraphic = aPicture;if (test = aGraphic-> GetPicture ()){test->Add(new Rectangle); }aGraphic = aRectangle;if (test = aGraphic-> GetPicture ()){test->Add(new Rectangle); //此次Add ()操作实际并不执行} 为了识别一个组件是基元还是组合,可按以下示例代码,定义一 个 查 询 函 数 GetPicture(),对其返回的组合安全地执行Add()和 Remove()操作:3.共享对象(.共享对象(Flyweight))模式模式::细粒度对象通常因为数量太大而难以用对象进行建模。 共享对象模式提供解决这个难题的有效手段例如:在文档编辑器程序中,文档对象可能是一个递归组合模式对象若将文档中的字符作为文档组合对象树的叶结点,可以将字符与文档中的图形、表格等嵌入部分的绘制和格式化作统一处理,并且在程序扩展,支持新字符集时不影响其它部分但是成千上万的字符对象将耗费大量内存,并产生难以接受的运行开销共享对象模式解决了这一问题,使大量细粒度对象的使用无须付出这些过于昂贵的代价一 个 共 享 对 象 (flyweight)可 以 在 多 个 上 下 文(context)对象中使用在每个具体的上下文对象中,flyweight作为一个独立的对象出现,在这一点上与非共享对象没有区别Flyweight的数据成员被看作它的“内部状态”,是只读的Flyweight的上下文对象为flyweight提供上下文信息,它们被看作flyweight的“外部状态”用户对象负责在必要的时候将外部状态传递给Flyweight以文档编辑器应用为例,它为字母表中每一个字母创建一个共享对象(flyweight)每个flyweight虽然只存储一个字母代码,但它在文档中的位置和排版风格可以由排版算法或格式化命令决定。 逻辑上,文档中的字符每次出现都会有一个对象与之对应物理上,同一字符共享一个flyweight对象,即同一字符每次出现都指向同一实例,该实例位于对象的共享池中 flyweight 对象池对象池abcdeihgflkjmnopqrvutsyxwzaRowaColumnaRowaRowAmerica4.代理(.代理(Proxy))模式模式代理模式的原型可以参考文件缓冲区与磁盘文件的关系正如只有在需要访问某个磁盘文件时才为它建立缓冲区的道理一样,在面向对象系统中只有在确实需要某个对象时才对它进行创建和初始化例如,当需要打开一个多媒体文档时,如果一次性打开其中包含的所有对象,往往造成不必要的巨大开销,因为这些对象在文档中并不是同时可见的,没有必要同时创建它们此时有必要的是在文档中为这些暂时不需要创建的对象设置一个开销极小的代理对象由它负责在需要时创建由它代理的主体对象realSubjectRealSubjectRequest()…ClientProxyRequest(){ … realSubject-> Request();…}SubjectRequest()…(a) 代理模式结构aClientsubjectaProxyrealSubjectaRealSubject(b)运行时刻的对象代理关系图13.5 代理模式的结构和对象间的关系一一般般来来说说,,在在需需要要利利用用一一个个更更通通用用的的对对象象指指针针来来代代替替较较为为专专门门化化的的对对象象指指针针的的时时候候就就应应该该使使用用代代理理模模式式。 比比如如,,除除了了以以上上所所述述的的多多媒媒体体对对象象的的代代理理外外,,还还可可以以为为不不同同地地址址空空间间的的对对象象提提供供本本地地代代理理,,为为需需要要提提供供多多种种访访问权限的对象提供保护代理等等问权限的对象提供保护代理等等智能代理是代理模式的高级形式智能代理是代理模式的高级形式普通代理通过简单的指针替换实现代理任务,智能代理还可以在访问被代理对象时执行一些附加的操作,如:在第一次引用一个持久对象时,将它装入内存;在访问一个实际对象前,检查它是否被加锁,以防止冲突;对对象进行引用计数管理,当对象不再被引用时自动将它释放智智能能代代理理的的一一种种具具体体实实现现方方式式是是重重载载C++的的指指针针运运算算符符->这这样样就就可可以以在在通通过过指指针针访访问问被被代代理理对对象象时时,,执执行行一一些些附附加的操作加的操作13.2.3 与对象行为相关的模式与对象行为相关的模式行行为为((behavior)是是指指对对象象对对请请求求的的可可预预知知反反应应与与对对象象行行为为相相关关的的模模式式描描述述对对象象之之间间的的通通信信关关系系,,处处理理各各种种在在运运行行时时难难以以跟跟踪踪的的复复杂杂控控制制流流,,描描述述一一组组对对象象怎怎样样相相互互协协作作以以完完成成其中任一其中任一对对象都无法象都无法单单独完成的任独完成的任务务。 5.职责链(.职责链(Chain of Responsibility))模式:模式:职责链模式建立一些对象间的链接关系,使请求沿着该链传递,直到链上的某一个对象处理它为止链上的多个对象都有机会接收和处理请求,从而避免请求的发送者和接收者之间的耦合关系例如图形用户界面中的上下文有关帮助机制,用户可在当前界面以多种方式提交请求,启动帮助帮助信息窗口以含有进一步帮助的链接信息这种链接式对象请求处理通常从第一个接收请求的对象开始,或者自己处理该请求,或者转发给链中下一个候选对象 HandlerRequest()Concreat1Request()Concreat2Request()Clientsuccessor(a) 职责链结构aClientaHandleraConcreatsuccessoraConcreatsuccessor(b) 职责链对象链接图13.6 职责链模式结构及其对象结构实现职责链的代码示例:class Handler{public:Handler(Handler* sp): successor(sp){}virtual void Request();private: Handler* successor;};这是一个事件处理句柄接口,其子类可以定义自己的Request()实现。 基类的缺省实现如下:virtual void Handler::Request(){ if (successor)successor-> Request();}这是一个事先设定的固定的链,只要后继存在就会无条件转发若在Request()中用不同的请求参数来分派请求,可以实现更灵活的链接void Request(AbstrctRequest* theRequest) { switch(theRequest->Getkind()) {case 1:Request((AbstrctRequest *) theRequest);break;case 2://…default://…break; }} 6.命令(.命令(Command))模式模式命令模式又称事务(Transaction)模式,命令模式用于封装向某个对象的请求所谓请求,就是应用程序的操作人员通过图形用户界面(GUI)构件如按钮、图标、菜单项等发出的操作命令命令模式通过在请求调用对象和请求的执行对象之间增加一个Command中间对象,用以消解多对多复杂性aReceiverClientConcreateCommandExecute(){aReceive→Action()}stateCommandExecute()ReceiverAction()Trigger Clicked(){aCommand→Execute()}一个触发器(Ttrigger)可以是任何一种GUI控件,不同的触发器对象可以发出同一条命令 一个GUI控件在设计时不可能知道该控件的作用者对象。 如一个磁(存)盘图标按钮本身并不需要知道存盘命令执行时是对一个文本文件存盘还是对一个图形文件存盘命令模式通过将请求本身变成一个对象,使得工具箱对象可向未指定的应用对象发请求,以不同的请求对客户参数化使多种类型的控件共享抽象类Command的某个子类的一个实例 代理CommandExecute()QuitCommandExecute() SaveCommandExecute()PasteCommandExecute()If document is modifiedsave→Execute()else quit the application命令模式的关键是抽象的Command类它定义了一个抽象接口,其中最主要的是Execute()操作Command的实现类将请求的接收者作为实例变量,通过重定义的Execute()指定接收者应该完成的动作比如,一个Paste命令以一个文档编辑器作为其接收者 图13.9 宏命令递归组合ConcreateCommandExecute(){ for all c in commandsc→Execute() }CommandExecute()commands命令模式也是实现实现事务功能的基础。 一个事务可以看作一条宏命令,即一个请求触发封装在一起的多个命令宏命令可以对Command类用递归组合模式实现 除了上述特点外,命令模式还可以配合其它模式实现:(1) 上下文相关菜单(动态绑定请求);(2) 实现撤消重做功能:在Command接口中,除了Execute()外还可定义Unexecute()等方法。
