logo资料库

design_patterns(中文版) .PDF

第1页 / 共254页
第2页 / 共254页
第3页 / 共254页
第4页 / 共254页
第5页 / 共254页
第6页 / 共254页
第7页 / 共254页
第8页 / 共254页
资料共254页,剩余部分请下载后查看
下载第1章引言设计面向对象软件比较困难�而设计可复用的面向对象软件就更加困难。你必须找到相关的对象�以适当的粒度将它们归类�再定义类的接口和继承层次�建立对象之间的基本关系。你的设计应该对手头的问题有针对性�同时对将来的问题和需求也要有足够的通用性。你也希望避免重复设计或尽可能少做重复设计。有经验的面向对象设计者会告诉你�要一下子就得到复用性和灵活性好的设计�即使不是不可能的至少也是非常困难的。一个设计在最终完成之前常要被复用好几次�而且每一次都有所修改。有经验的面向对象设计者的确能做出良好的设计�而新手则面对众多选择无从下手�总是求助于以前使用过的非面向对象技术。新手需要花费较长时间领会良好的面向对象设计是怎么回事。有经验的设计者显然知道一些新手所不知道的东西�这又是什么呢�内行的设计者知道�不是解决任何问题都要从头做起。他们更愿意复用以前使用过的解决方案。当找到一个好的解决方案�他们会一遍又一遍地使用。这些经验是他们成为内行的部分原因。因此�你会在许多面向对象系统中看到类和相互通信的对象�c o m m u n i c a t i n go b j e c t�的重复模式。这些模式解决特定的设计问题�使面向对象设计更灵活、优雅�最终复用性更好。它们帮助设计者将新的设计建立在以往工作的基础上�复用以往成功的设计方案。一个熟悉这些模式的设计者不需要再去发现它们�而能够立即将它们应用于设计问题中。以下类比可以帮助说明这一点。小说家和剧本作家很少从头开始设计剧情。他们总是沿袭一些业已存在的模式�像“悲剧性英雄”模式(《麦克白》、《哈姆雷特》等)或“浪漫小说”模式(存在着无数浪漫小说)。同样地�面向对象设计员也沿袭一些模式�像“用对象表示状态”和“修饰对象以便于你能容易地添加/删除属性”等。一旦懂得了模式�许多设计决策自然而然就产生了。我们都知道设计经验的重要价值。你曾经多少次有过这种感觉—你已经解决过了一个问题但就是不能确切知道是在什么地方或怎么解决的�如果你能记起以前问题的细节和怎么解决它的�你就可以复用以前的经验而不需要重新发现它。然而�我们并没有很好记录下可供他人使用的软件设计经验。这本书的目的就是将面向对象软件的设计经验作为设计模式记录下来。每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和重复出现的设计。我们的目标是将设计经验以人们能够有效利用的形式记录下来。鉴于此目的�我们编写了一些最重要的设计模式�并以编目分类的形式将它们展现出来。设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。设计模式帮助你做出有利于系统复用的选择�避免设计损害了系统复用性。通过提供一个显式类和对象作用关系以及它们之间潜在联系的说明规范�设计模式甚至能够提高已有系统的文档管理和系统维护的有效性。简而言之�设计模式可以帮助设计者更快更好地完成系统设计。本书中涉及的设计模式并不描述新的或未经证实的设计�我们只收录那些在不同系统中
多次使用过的成功设计。这些设计的绝大部分以往并无文档记录�它们或是来源于面向对象设计者圈子里的非正式交流�或是来源于某些成功的面向对象系统的某些部分�但对设计新手来说�这些东西是很难学得到的。尽管这些设计不包含新的思路�但我们用一种新的、便于理解的方式将其展现给读者�即�具有统一格式的、已分类编目的若干组设计模式。尽管该书涉及较多的内容�但书中讨论的设计模式仅仅包含了一个设计行家所知道的部分。书中没有讨论与并发或分布式或实时程序设计有关的模式�也没有收录面向特定应用领域的模式。本书并不准备告诉你怎样构造用户界面、怎样写设备驱动程序或怎样使用面向对象数据库�这些方面都有自己的模式�将这些模式分类编目也是件很有意义的事。1.1 什么是设计模式Christopher Alexander说过�“每一个模式描述了一个在我们周围不断重复发生的问题�以及该问题的解决方案的核心。这样�你就能一次又一次地使用该方案而不必做重复劳动”[ A I S + 7 7�第1 0页]。尽管A l e x a n d e r所指的是城市和建筑模式�但他的思想也同样适用于面向对象设计模式�只是在面向对象的解决方案里�我们用对象和接口代替了墙壁和门窗。两类模式的核心都在于提供了相关问题的解决方案。一般而言�一个模式有四个基本要素�1. 模式名称�pattern name�一个助记名�它用一两个词来描述模式的问题、解决方案和效果。命名一个新的模式增加了我们的设计词汇。设计模式允许我们在较高的抽象层次上进行设计。基于一个模式词汇表�我们自己以及同事之间就可以讨论模式并在编写文档时使用它们。模式名可以帮助我们思考�便于我们与其他人交流设计思想及设计结果。找到恰当的模式名也是我们设计模式编目工作的难点之一。2. 问题(problem) 描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果�它可能描述了特定的设计问题�如怎样用对象表示算法等。也可能描述了导致不灵活设计的类或对象结构。有时候�问题部分会包括使用模式必须满足的一系列先决条件。3. 解决方案(solution) 描述了设计的组成成分�它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板�可应用于多种不同场合�所以解决方案并不描述一个特定而具体的设计或实现�而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合�类或对象组合�来解决这个问题。4. 效果(consequences) 描述了模式应用的效果及使用模式应权衡的问题。尽管我们描述设计决策时�并不总提到模式效果�但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。软件效果大多关注对时间和空间的衡量�它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一�所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响�显式地列出这些效果对理解和评价这些模式很有帮助。出发点的不同会产生对什么是模式和什么不是模式的理解不同。一个人的模式对另一个人来说可能只是基本构造部件。本书中我们将在一定的抽象层次上讨论模式。《设计模式》并不描述链表和h a s h表那样的设计�尽管它们可以用类来封装�也可复用�也不包括那些复杂的、特定领域内的对整个应用或子系统的设计。本书中的设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述。一个设计模式命名、抽象和确定了一个通用设计结构的主要方面�这些设计结构能被用2设计模式�可复用面向对象软件的基础下载
来构造可复用的面向对象设计。设计模式确定了所包含的类和实例�它们的角色、协作方式以及职责分配。每一个设计模式都集中于一个特定的面向对象设计问题或设计要点�描述了什么时候使用它�在另一些设计约束条件下是否还能使用�以及使用的效果和如何取舍。既然我们最终要实现设计�设计模式还提供了C++ 和S m a l l t a l k示例代码来阐明其实现。虽然设计模式描述的是面向对象设计�但它们都基于实际的解决方案�这些方案的实现语言是Smalltalk 和C + +等主流面向对象编程语言�而不是过程式语言( P a s c a l、C、A d a )或更具动态特性的面向对象语言( C L O S、D y l a n、S e l f )。我们从实用角度出发选择了Smalltalk 和C + +�因为在这些语言的使用上�我们积累了许多经验�况且它们也变得越来越流行。程序设计语言的选择非常重要�它将影响人们理解问题的出发点。我们的设计模式采用了Smalltalk 和C + +层的语言特性�这个选择实际上决定了哪些机制可以方便地实现�而哪些则不能。若我们采用过程式语言�可能就要包括诸如“继承”、“封装”和“多态”的设计模式。相应地�一些特殊的面向对象语言可以直接支持我们的某些模式�例如�C L O S支持多方法�m u l t i - m e t h o d�概念�这就减少了Vi s i t o r模式的必要性。事实上�S m a l l t a l k和C + +已有足够的差别来说明对某些模式一种语言比另一种语言表述起来更容易一些(参见5 . 4节Iterator 模式)。1.2 Smalltalk MVC中的设计模式在S m a l l t a l k - 8 0中�类的模型/视图/控制器�M o d e l / Vi e w / C o n t r o l l e r�三元组( M V C )被用来构建用户界面。透过MVC 来看设计模式将帮助我们理解“模式”这一术语的含义。M V C包括三类对象。模型M o d e l是应用对象�视图Vi e w是它在屏幕上的表示�控制器C o n t r o l l e r定义用户界面对用户输入的响应方式。不使用M V C�用户界面设计往往将这些对象混在一起�而M V C则将它们分离以提高灵活性和复用性。M V C通过建立一个“订购/通知”协议来分离视图和模型。视图必须保证它的显示正确地反映了模型的状态。一旦模型的数据发生变化�模型将通知有关的视图�每个视图相应地得到刷新自己的机会。这种方法可以让你为一个模型提供不同的多个视图表现形式�也能够为一个模型创建新的视图而无须重写模型。下图显示了一个模型和三个视图�为了简单起见我们省略了控制器�。模型包含一些数据值�视图通过电子表格、柱状图、饼图这些不同的方式来显示这些数据。当模型的数据发生变化时�模型就通知它的视图�而视图将与模型通信以访问这些数据值。第1章引言3下载视图模型
表面上看�这个例子反映了将视图和模型分离的设计�然而这个设计还可用于解决更一般的问题�将对象分离�使得一个对象的改变能够影响另一些对象�而这个对象并不需要知道那些被影响的对象的细节。这个更一般的设计被描述成O b s e r v e r�5 . 7�模式。M V C的另一个特征是视图可以嵌套。例如�按钮控制面板可以用一个嵌套了按钮的复杂视图来实现。对象查看器的用户界面可由嵌套的视图构成�这些视图又可复用于调试器。M V C用Vi e w类的子类—C o m p o s i t e Vi e w类来支持嵌套视图。C o m p o s i t e Vi e w类的对象行为上类似于Vi e w类对象�一个组合视图可用于任何视图可用的地方�但是它包含并管理嵌套视图。上例反映了可以将组合视图与其构件平等对待的设计�同样地�该设计也适用于更一般的问题�将一些对象划为一组�并将该组对象当作一个对象来使用。这个设计被描述为C o m p o s i t e�4 . 3�模式�该模式允许你创建一个类层次结构�一些子类定义了原子对象�如B u t t o n�而其他类定义了组合对象�C o m p o s i t e Vi e w��这些组合对象是由原子对象组合而成的更复杂的对象。M V C允许你在不改变视图外观的情况下改变视图对用户输入的响应方式。例如�你可能希望改变视图对键盘的响应方式�或希望使用弹出菜单而不是原来的命令键方式。M V C将响应机制封装在C o n t r o l l e r对象中。存在着一个C o n t r o l l e r的类层次结构�使得可以方便地对原有C o n t r o l l e r做适当改变而创建新的C o n t r o l l e r。Vi e w使用C o n t r o l l e r子类的实例来实现一个特定的响应策略。要实现不同的响应策略只要用不同种类的C o n t r o l l e r实例替换即可。甚至可以在运行时刻通过改变Vi e w的C o n t r o l l e r来改变Vi e w对用户输入的响应方式。例如�一个Vi e w可以被禁止接收任何输入�只需给它一个忽略输入事件的C o n t r o l l e r。Vi e w - C o n t r o l l e r关系是S t r a t e g y�5 . 9�模式的一个例子。一个策略是一个表述算法的对象。当你想静态或动态地替换一个算法�或你有很多不同的算法�或算法中包含你想封装的复杂数据结构�这时策略模式是非常有用的。M V C还使用了其他的设计模式�如�用来指定视图缺省控制器的Factory Method(3.3)和用来增加视图滚动的D e c o r a t o r ( 4 . 4 )。但是M V C的主要关系还是由O b s e r v e r、C o m p o s i t e和S t r a t e g y三个设计模式给出的。1.3 描述设计模式我们怎样描述设计模式呢�图形符号虽然很重要也很有用�却还远远不够�它们只是将设计过程的结果简单记录为类和对象之间的关系。为了达到设计复用�我们必须同时记录设计产生的决定过程、选择过程和权衡过程。具体的例子也是很重要的�它们让你看到实际的设计。我们将用统一的格式描述设计模式�每一个模式根据以下的模板被分成若干部分。模板具有统一的信息描述结构�有助于你更容易地学习、比较和使用设计模式。模式名和分类模式名简洁地描述了模式的本质。一个好的名字非常重要�因为它将成为你的设计词汇表中的一部分。模式的分类反映了我们将在1 . 5节介绍的方案。意图是回答下列问题的简单陈述�设计模式是做什么的�它的基本原理和意图是什么�它解4设计模式�可复用面向对象软件的基础下载
决的是什么样的特定设计问题�别名模式的其他名称。动机用以说明一个设计问题以及如何用模式中的类、对象来解决该问题的特定情景。该情景会帮助你理解随后对模式更抽象的描述。适用性什么情况下可以使用该设计模式�该模式可用来改进哪些不良设计�你怎样识别这些情况�结构采用基于对象建模技术�O M T�[ R B P + 9 1 ]的表示法对模式中的类进行图形描述。我们也使用了交互图[ J C J O 9 2�B O O 9 4 ]来说明对象之间的请求序列和协作关系。附录B详细描述了这些表示法。参与者指设计模式中的类和/或对象以及它们各自的职责。协作模式的参与者怎样协作以实现它们的职责。效果模式怎样支持它的目标�使用模式的效果和所需做的权衡取舍�系统结构的哪些方面可以独立改变�实现实现模式时需要知道的一些提示、技术要点及应避免的缺陷�以及是否存在某些特定于实现语言的问题。代码示例用来说明怎样用C + +或S m a l l t a l k实现该模式的代码片段。已知应用实际系统中发现的模式的例子。每个模式至少包括了两个不同领域的实例。相关模式与这个模式紧密相关的模式有哪些�其间重要的不同之处是什么�这个模式应与哪些其他模式一起使用�附录提供的背景资料将帮助你理解模式以及关于模式的讨论。附录A给出了我们使用的术语列表。前面已经提到过的附录B则给出了各种表示法�我们也会在以后的讨论中简单介绍它们。最后�附录C给出了我们在例子中使用的各基本类的源代码。1.4 设计模式的编目从第3章开始的模式目录中共包含2 3个设计模式。它们的名字和意图列举如下�以使你有个基本了解。每个模式名后括号中标出模式所在的章节(我们整本书都将遵从这个约定)。Abstract Factory( 3 . 1 )�提供一个创建一系列相关或相互依赖对象的接口�而无需指定它们具体的类。第1章引言5下载
A d a p t er ( 4 . 1 )�将一个类的接口转换成客户希望的另外一个接口。A d a p t e r模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。B r i d g e( 4 . 2 )�将抽象部分与它的实现部分分离�使它们都可以独立地变化。B u i l d e r( 3 . 2 )�将一个复杂对象的构建与它的表示分离�使得同样的构建过程可以创建不同的表示。Chain of Responsibility( 5 . 1 )�为解除请求的发送者和接收者之间耦合�而使多个对象都有机会处理这个请求。将这些对象连成一条链�并沿着这条链传递该请求�直到有一个对象处理它。C o m m a n d( 5 . 2 )�将一个请求封装为一个对象�从而使你可用不同的请求对客户进行参数化�对请求排队或记录请求日志�以及支持可取消的操作。C o m p o s i t e( 4 . 3 )�将对象组合成树形结构以表示“部分-整体”的层次结构。C o m p o s i t e使得客户对单个对象和复合对象的使用具有一致性。D e c o r a t o r( 4 . 4 )�动态地给一个对象添加一些额外的职责。就扩展功能而言�D e c o r a t o r模式比生成子类方式更为灵活。F a c a d e( 4 . 5 )�为子系统中的一组接口提供一个一致的界面�F a c a d e模式定义了一个高层接口�这个接口使得这一子系统更加容易使用。Factory Method( 3 . 3 )�定义一个用于创建对象的接口�让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。F l y w e i g h t( 4 . 6 )�运用共享技术有效地支持大量细粒度的对象。I n t e r p r e t e r( 5 . 3 )�给定一个语言, 定义它的文法的一种表示�并定义一个解释器, 该解释器使用该表示来解释语言中的句子。I t e r a t o r( 5 . 4 )�提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。M e d i a t o r( 5 . 5 )�用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用�从而使其耦合松散�而且可以独立地改变它们之间的交互。M e m e n t o( 5 . 6 )�在不破坏封装性的前提下�捕获一个对象的内部状态�并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。O b s e r v e r( 5 . 7 )�定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。P r o t o t y p e( 3 . 4 )�用原型实例指定创建对象的种类�并且通过拷贝这个原型来创建新的对象。P r o x y( 4 . 7 )�为其他对象提供一个代理以控制对这个对象的访问。S i n g l e t o n( 3 . 5 )�保证一个类仅有一个实例�并提供一个访问它的全局访问点。S t a t e( 5 . 8 )�允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。S t r a t e g y (5 . 9 )�定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。Template Method( 5 . 1 0 )�定义一个操作中的算法的骨架�而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。6设计模式�可复用面向对象软件的基础下载
Vi s i t o r( 5 . 11 )�表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。1.5 组织编目设计模式在粒度和抽象层次上各不相同。由于存在众多的设计模式�我们希望用一种方式将它们组织起来。这一节将对设计模式进行分类以便于我们对各族相关的模式进行引用。分类有助于更快地学习目录中的模式�且对发现新的模式也有指导作用�如表1 - 1所示。表1-1 设计模式空间目的创建型结构型行为型范围类Factory Method(3.3)A d a p t e r (类) ( 4 . 1 )I n t e r p r e t e r ( 5 . 3 )Template Method(5.10)对象Abstract Factory(3.1)A d a p t e r (对象) ( 4 . 1 )Chain of Responsibility(5.1)B u i l d e r ( 3 . 2 )B r i d g e ( 4 . 2 )C o m m a n d ( 5 . 2 )P r o t o t y p e ( 3 . 4 )C o m p o s i t e ( 4 . 3 )I t e r a t o r ( 5 . 4 )S i n g l e t o n ( 3 . 5 )D e c o r a t o r ( 4 . 4 )M e d i a t o r ( 5 . 5 )F a c a d e ( 4 . 5 )M e m e n t o ( 5 . 6 )F l y w e i g h t ( 4 . 6 )O b s e r v e r ( 5 . 7 )P r o x y ( 4 . 7 )S t a t e ( 5 . 8 )S t r a t e g y ( 5 . 9 )Vi s i t o r ( 5 . 1 0 )我们根据两条准则(表1 - 1 )对模式进行分类。第一是目的准则�即模式是用来完成什么工作的。模式依据其目的可分为创建型�C r e a t i o n a l�、结构型( S t r u c t u r a l )、或行为型( B e h a v i o r a l )三种。创建型模式与对象的创建有关�结构型模式处理类或对象的组合�行为型模式对类或对象怎样交互和怎样分配职责进行描述。第二是范围准则�指定模式主要是用于类还是用于对象。类模式处理类和子类之间的关系�这些关系通过继承建立�是静态的�在编译时刻便确定下来了。对象模式处理对象间的关系�这些关系在运行时刻是可以变化的�更具动态性。从某种意义上来说�几乎所有模式都使用继承机制�所以“类模式”只指那些集中于处理类间关系的模式�而大部分模式都属于对象模式的范畴。创建型类模式将对象的部分创建工作延迟到子类�而创建型对象模式则将它延迟到另一个对象中。结构型类模式使用继承机制来组合类�而结构型对象模式则描述了对象的组装方式。行为型类模式使用继承描述算法和控制流�而行为型对象模式则描述一组对象怎样协作完成单个对象所无法完成的任务。还有其他组织模式的方式。有些模式经常会被绑在一起使用�例如�C o m p o s i t e常和I t e r a t o r或Vi s i t o r一起使用�有些模式是可替代的�例如�P r o t o t y p e常用来替代A b s t r a c tF a c t o r y�有些模式尽管使用意图不同�但产生的设计结果是很相似的�例如�C o m p o s i t e和D e c o r a t o r的结构图是相似的。还有一种方式是根据模式的“相关模式”部分所描述的它们怎样互相引用来组织设计模式。图1 - 1给出了模式关系的图形说明。第1章引言7下载
显然�存在着许多组织设计模式的方法。从多角度去思考模式有助于对它们的功能、差异和应用场合的更深入理解。图1-1 设计模式之间的关系1.6 设计模式怎样解决设计问题设计模式采用多种方法解决面向对象设计者经常碰到的问题。这里给出几个问题以及使用设计模式解决它们的方法。1.6.1 寻找合适的对象面向对象程序由对象组成�对象包括数据和对数据进行操作的过程�过程通常称为方法或操作。对象在收到客户的请求(或消息)后�执行相应的操作。8设计模式�可复用面向对象软件的基础下载保存迭代状态创建组合给对象增加职责改变外表改变内容定义算法步骤动态地配置工厂用工厂方法实现单个实例单个实例增加操作增加操作共享组合共享策略定义语法共享终结符共享状态定义遍历定义链避免滞后枚举子女使用组合命令
分享到:
收藏