好书系列之-设计模式:可复用面向对象软件的基础 1


第1章引言
设计面向对象软件比较困难,而设计可复用的面向对象软件就更加困难。你必须找到相
关的对象,以适当的粒度将它们归类,再定义类的接口和继承层次,建立对象之间的基本关
系。你的设计应该对手头的问题有针对性,同时对将来的问题和需求也要有足够的通用性。
你也希望避免重复设计或尽可能少做重复设计。有经验的面向对象设计者会告诉你,要一下
子就得到复用性和灵活性好的设计,即使不是不可能的至少也是非常困难的。一个设计在最
终完成之前常要被复用好几次,而且每一次都有所修改。
有经验的面向对象设计者的确能做出良好的设计,而新手则面对众多选择无从下手,总
是求助于以前使用过的非面向对象技术。新手需要花费较长时间领会良好的面向对象设计是
怎么回事。有经验的设计者显然知道一些新手所不知道的东西,这又是什么呢?
内行的设计者知道:不是解决任何问题都要从头做起。他们更愿意复用以前使用过的解
决方案。当找到一个好的解决方案,他们会一遍又一遍地使用。这些经验是他们成为内行的
部分原因。因此,你会在许多面向对象系统中看到类和相互通信的对象( c o m m u n i c a t i n g
o 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 描述设计模式
我们怎样描述设计模式呢?图形符号虽然很重要也很有用,却还远远不够,它们只是将
设计过程的结果简单记录为类和对象之间的关系。为了达到设计复用࿰

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值