《Design Patterns Explained》读书笔记

本书通过全新的视角解析设计模式,深入探讨面向对象设计原则。通过案例研究,讲解如何利用设计模式简化软件设计并提高代码质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Design Patterns Explained: A New Perspective onObject-Oriented Design

作者:Alan Shalloway, James R. Trott

 

第一章 面向对象泛型

1.2 面向对象泛型之前:功能分解

功能分解是一种处理复杂问题的自然方法。功能分解方法的一个问题在于,它通常会导致一个“主”程序负责控制子程序,这是将功能分解为多个子功能的自然结果。但是,主程序所承受的责任太多了:要确保一切正常工作,还要协调各函数并控制他们的先后顺序,因此经常会产生非常复杂的代码。功能分解的另一个问题在于,它在未未来可能出现的变化未雨绸缪方面,在对代码合适地改进方面,都于事无补。

我对作者这儿的说法不同意,不管是结构化还是面向对象,功能分解都是必须的,谁说面向对象就排斥功能分解了?!!如果你对做一件事情的先后顺序都搞不清楚,不管面向什么你都搞不定。

 

许多隐错都源于代码修改。

自己去验证这句断言吧。考虑这样的情景:想对代码进行修改,但又害怕这样做,因为你知道修改一个地方的代码可能会破坏其他地方。

这一点深有体会,如果一个成员在若干地方都在使用,这时修改就很没把握了。要是仅仅在一个地方,修改起来还是很容易的。但为什么会那么多地方使用呢,有没有办法控制仅仅在一个地方使用?

 

无论多么努力工作,无论分析做得多么好,也是永远无法从用户那里获得所有需求的,因为关于未来有太多未知,万物皆变化。不是吗,它们总是在变化之中......

对于阻止变化,我们无计可施。但是我们对变化本身却并非无能为力。

虽然需求在变,但是我们还是应该尽可能早地发掘最多的需求,如何发掘仍然是一个很大的挑战,昨天吃饭时和德哥讨论,他都没有一个好的办法,何况我呢?

 

1.3 需求问题

我还了解到,大多数开发人员都认为这是一件坏事。但是很少人能够编写可以很好地处理需求变更的代码。

需求之所以变化,有如下一个简单原因:

用户对自己需求的看法,会因为与开发人员的讨论,和看到软件新的可能性,而发生变化。

开发人员对用户问题领域的看法,会在开发软件的过程中,因为对它更加熟悉而发生变化。

其实不管是用户还是开发人员,都会因为熟悉程度加深而重新认识需求。有时老说TDD,如果我对知道我写的东西随时可能变化,我还写那么多测试用例干嘛?!

 

我可能无法知道什么将会变化,但是我能够猜到在哪里会变化。

最后,我认识到,虽然无法预测会发生什么变化,但是通常可以预期哪里会发生变化。面向对象的巨大优点之一,就是可以封装这些变化区域,从而更加容易地将代码与变化产生的影响隔离开来。

但是如果我无法猜到哪里会变化呢,或者说猜错了呢?咋办?比如告警窗口是放到任务下还是全局,我猜到了,那么该怎么办才能改变最小?

 

这并不意味着我们可以不去收集好的需求。

这句话印证了我前面说的:我们还是应该尽可能早地发掘最多的需求。

 

1.4 应对变化:使用功能分解

模块化肯定有助于提供代码的可理解性,而容易理解将使代码更容易维护。但是模块化并不总是有助于代码应对所有可能遇到的变化。

 

我认为,在维护和调试过程中,改正隐错只需要花费很少的时间。维护和调试的绝大多数时间都被用于努力弄清代码的运作机理、寻找隐错和防止出现不良副作用上了,真正的改正时间却相当短。

 

1.5 应对需求变更

日常生活中人们如何做事?

这一点我深有同感,把编程问题映射到现实生活中,你会得到更多的灵感,或者能找到更好的解决方案。比如书上的例子,观察者模式等

 

其中最大的区别就是这种责任的转移。在第一种情况下,你要对一切负责;而在第二种情况下,学生对自己的行为负责。两种情况下,要实现的目的相同,但组织方式差异很大。

 

1.6 面向对象范型

面向对象范型以对象概念为中心,一切都集中在对象上。编写代码时是围绕对象而非函数进行组织的。

 

使用对象的优点在于,可以定义自己负责自己的事物。对象天生就知道自己的类型。对象中的数据能够告诉它自己的状态如何,而对象中的代码能够使它正确工作。

在这种情况下,对象是通过寻找在问题领域中的实体而被发现的。然后再通过查看这些实体需要做些什么,为每个对象确定责任(或者称方法)。

如何发现对象是个技术活,看来还需要再仔细研读《UML和模式应用》那本书。

 

还可以用Martin Fowler的视角框架来观察对象:

在概念层次上,对象是一组责任。

在规约层次上,对象是一组可以被其他对象或自己调用的方法(也称行为)。

在实现层次上,对象是代码和数据,以及它们之间的计算交互。

糟糕的是,人们对面向对象设计和讨论更多的是停留在实现层次上--也就是只考虑代码和数据,对概念层次和规约层次重视很不够。然而,从后两种层次去思考对象其实也具有巨大的效力。

我们很少从概念层次上思考,所以我们抽象的对象有时很不合适。

 

抽象类经常被描述为“不能实例化的类”。这个定义本身没错--在实现层次上。但是局限性太大了。在概念层次上定义抽象类会更有帮助。在概念层次,抽象类就是其他类的占位符。也就是说,抽象类为我们提供了一种方法,能够给一组相关的类赋予一个名字。这使我们能够将这一组相关类看成一个概念。

 

在面向对象范型中,必须总是从概念、规约和实现所有三个视角层次来思考问题。

 

1.7 面向对象程序设计实践

ShapeDataBase、Shape(抽象类)、Square(派生自Shape类)、Circle(派生自Shape类),Collection、Display

 

复习题

 

 

第2章 UML

2.2 什么是UML

 

2.3 为什么使用UML

UML主要是用来交流的--与我自己、与我的小组成员、与我的客户。在软件开发领域中糟糕的(不完整的或者不准确的)需求无处不在,而UML为我们提供了提高需求质量的工具。

这一点和《大道至简》上说的完全一致,UML主要是用来交流的,而不是画出来摆酷的。

 

UML不仅仅是描述面向对象设计的更佳方法,它还使设计人员能够仔细考虑其设计中类之间的关系(因为必须将设计写下来)。

 

2.4 类图

公共、保护、私有分别用什么符号表示?

答:+ # -

 

继承、组合、聚集、使用(依赖)分别怎么表示?

答:实线空三角、实线实棱形、实线空棱形、虚线箭头

注意,EA中的Gerneralization就是指继承。

 

最容易搞混的是组合和聚集,

对于组合,比如发动机和汽车是组合关系,也就是说去掉发动机汽车就不是汽车了。

对于聚集,比如飞机和飞机场是聚集关系,也就是说,飞机场不会因为没有飞机就不是飞机场了,飞机场仅仅是给飞机提供了一个停靠的聚集地。还比如任务和任务管理器,即相当于容器。

组合和聚集都有生命周期管理的职责。

 

如果既不是继承,也不是组合和聚集,那么就是使用了。比如界面类依赖于CRunHandler类。对于使用,使用者是不负责管理被使用者生命周期的这是使用与组合聚集最大的区别。

 

有时我们容易搞不清方向,我们可以在继承、组合、聚集、使用(依赖)后加一个字,画图或者看图的时候都固定从没有箭头那端开始,比如B依赖于A,则应该是B------->A(B  >(依赖于)  A)

其实,只要方向正确,然后把实线空三角、实线实棱形、实线空棱形、虚线箭头依次翻译成继承、组合、聚集、使用(依赖)就对了。

 

抽象类如何表示?

答:Shape类的名字是用斜体表示的,说明它是一个抽象类。

 

组合和聚集都有“一个对象包含一个或多个对象”的意思,所以有重数关系,比如1对n等。

我们可以认为组合是一种非共享的关联,被包含对象的生命周期由包含对象控制。

这儿说的跟我上面的理解差不多,但是还是有差别,即聚集不管理生命周期,比如任务管理器只是提供接口让外界去控制任务的生命周期,而组合一般是生命周期相同的。

 

2.5 交互图

类图可以表示类之间的静态关系,换句话说,类图不能表示任何活动。

表示对象间如何交互的UML图称为交互图。最常用的交互图是顺序图。

本书介绍的很简单,是不是很少用啊?呵呵。我觉得这个图画起来太麻烦了,要是我要经常修改或者消息太多,岂不是麻烦死,还不如直接改代码呢!待继续了解

 

2.6 小结

UML既能够充实设计,又能够用于设计的交流。不要太担心要“正确地”画图。要考虑的是什么方式最有利于交流设计中的概念。换句话说:

如果你认为又什么东西需要说,可以用注释来表达。

如果你对一个图标或符号不太确定,必须查手册才能确定其意义,还是加一条注释来解释。毕竟,其他人有可能也不清楚它的意义。

清晰为好。

当然,你也意味着你应该以规范的方式使用UML--那样无法正常交流。在画图的时候,只考虑要传达的思想即可。

 

第3章 对代码灵活性要求很高的问题

没看明白,也不太想研究,因为我看书的核心是学会设计模式。

 

第4章 标准的面向对象解决方案

这一章我觉得有几条建议还不错,其他关于业务的内容就忽略了:

1.死死盯住球

打垒球的一个基本原则,就是死死盯住球。不要因为次要的细节而分散注意力。将精力集中在手头最重要的任务上,现在要处理的事情上。

对于软件开发人员来说,这同样是金玉良言。正确的分析和设计要求我们在互相矛盾的关注点之间找到平衡。必须解决问题的哪些方面是设计的重点,或者应该让系统防范哪些变化。寻找平衡点必须成为做出设计决策的第一要务。不要让其他细节转移了你的注意力。

在本例中,我们已经预见到未来CAD/CAM程序会发生变化。而这种变化将产生重大影响,因此我们必须予以控制。这就是我们要盯住的“球”。

2.分析陷阱:过早过多地关注细节

分析人员都可能犯的一个常见错误是:在开发过程中过早地深入细节。这很自然,因为处理细节比较容易。细节的解决方案总是显而易见,但并非肯定是最好的入手点。细节问题的处理应该尽可能推后。

在本例中,我已经实现了一个目标:为部件信息提供了一个公用的API。我还从责任的角度定义了对象。但是,这样做的代价是所有东西都要作为特例。如果有了新的特例,我还要如法炮制地将她实现一遍。因此,维护代价太高了。

3.留意你的直觉

令人惊讶的是,本能直觉能够很好地评判设计质量。我建议开发人员应该学会倾听自己的心声。

这里的“本能直觉”是指看到某些不喜欢的东西时身体的第一感觉。我知道这听上去好像不太科学(实际确实如此),但我的经验不断证明:当我直觉上不喜欢某个设计时,更好的设计肯定还隐藏在某个角落里。当然,有时附近会有几个不同的角落,我并不是总能确定哪个角落里会隐藏着更好的设计。

 

第5章 设计模式简介

5.2 设计模式源自建筑学和人类学

每个模式描述必须有4个部分:

模式的名称

模式的目的,即要解决的问题

实现方法

为了实现该模式我们必须要考虑的限制和约束因素

 

5.4 为什么学习设计模式

复用解决方案

确立通用术语

 

5.5 学习设计模式的其他好处

《设计模式》一书对优秀面向对象设计的策略提出了一些建议。其中包括以下几点:

按接口编程

尽量用聚合代替继承

找出变化并封装之

 

5.6 小结

本章探讨了学习设计模式的理由。学习模式有助于:

对不断重复出现问题,复用既有的、高质量的解决方案

确立通用的术语,改善团队内的沟通

提升思考层次

判断设计是否正确,而不仅仅是能够奏效

改善个人学习和团队学习

提高代码的可修改性和可维护性

采用更佳设计方案,即使没有明确使用模式

发现巨型继承层次结构的替代方案

 

第6章 Facade模式

《设计》模式一书中对Facade模式的意图是这样叙述的:

为子系统中的一组接口提供一个统一接口。Facade模式定义了一个更高层的接口,使子系统更加容易使用。

这段话大致是在说:我们需要用一种比原来的方式更简单的办法与系统交互。

 

Facade模式:关键特征

意图     希望简化原有系统的使用方式。需要定义自己的接口。

问题     只需要使用某个复杂系统的子集,或者,需要以一种特殊的方式与系统交互。

解决方案  Facade为原有系统的客户提供了一个新的接口。

参与者与协作者 为客户提供的一个简化接口,使系统更容易使用。

效果     Facade模式简化了对所需子系统的使用过程。但是,由于Facade并不完整,因此客户可能无法使用某些功能。

实现     定义一个(或多个)具备所需接口的新类。让新的类使用原有的系统。

 

6.6 小结

Facade模式可以应用于:

不需要使用一个复杂系统的所有功能,而且可以创建一个新的类,包含访问系统的所有规则。如果只需要使用系统的部分功能(这是通常的情况),那么你为新类所创建的API将比原来的简单得多。

希望封装或者隐藏原系统。

希望使用原系统的功能,而且还希望增加一些新的功能。

编写新类的成本小于所有人学会使用或者未来维护原系统上所需的成本。

 

在UCC中,CXMLReader可能算是Facade模式的一个很好的例子。上面的每一句话,除了绿色部分,都完全可以用CXMLReader去解释。其实在工作中,我们已经用了这个模式,只是自己不知道而已,呵呵!

 

第7章 Adapter模式

《设计模式》一书对Adapter模式的意图是这样叙述的:

将一个类的接口转换成客户希望的另一个接口。Adapter模式使原本不兼容而不能一起工作的类可以一起工作。

 

Adapter模式:关键特征

意图   使控制范围之外的一个原有对象与某个接口匹配

问题   系统的数据和行为都正确,但接口不符。通常用于必须从抽象类派生时。

解决方案 Adapter模式提供了jv有所需接口的包装类

参与者与协作者 Adapter改变了Adaptee的接口,使Adapteee与Adapter的基类Target匹配。这样Client就可以使用Adaptee了,好像它是Target类型。

效果   Adapter模式使原有对象能够适应新的类结构,不受其接口限制。

实现   将原有类包含在另一个类之中。让包含类与需要的接口匹配,调用被包容类的方法。

 

让我们看看Facade模式和Adapter模式涉及的一些不同因素:

在两个模式中,都存在既有的类。

但是在Facade模式中,我无须按某个接口进行设计(相当于客户使用的方式还没定);而在Adapter模式中,则必须按某个特定接口进行设计(相当于客户使用的方式已经确定下来了)。

在Facade模式中我不需要多态行为,而在Adapter模式中多态行为可能是需要的(因为有可能接口是一个抽象类)。

Facade模式的动机是简化接口。而在Adapter模式中,尽管也是越简单越好,但是设计必须遵循一个已有的接口,不能简化任何东西,即使可能存在更简单的接口。

 

 

第8章 开拓视野

8.2 对象:传统看法与新看法

更有意义的定义应该从概念视角出发--对象是具有责任的一个实体。这些责任定义了对象的行为。有时我们还将对象视为特定行为的实体。

责任体现在哪儿呢,就体现在公开接口上。这个定义让我们明确了一个观念,首先你应该关注的是接口(即责任或者说行为),然后才是对象的内部实现。

 

8.3 封装:传统看法与新看法

封装应该被视为“任何形式的隐藏”。换句话说,可以是隐藏数据,但还可以隐藏以下各种东西:

实现细节

派生类

设计细节

实例化规则

 

类型的封装是通过多态使用具有派生类的抽象类(或者具有多种实现的接口)实现。使用该抽象类的客户代码无需知道派生类的实际类型,这正是《设计模式》一书中封装的通常含义。

在UCC,SIP呼叫器中,大师,黄建搞了大量的所谓封装,什么东西都从一个抽象类继承,要添加一个函数总是需要到两个地方添加,要想在客户代码中调用处进去看看如何实现还老是跳错地方,啥都看不到,麻烦得很。我觉得他们肯定片面的理解了封装,封装一定有其适用的条件,而不是哪儿都用。比如,在SIP呼叫器中,我根本就算不上是任务管理的客户,我需要知道它的实现,有可能我还要参与任务管理的代码修改,并且它的接口还老是不稳定,这时,采用封装除了影响编码效率之外,根本没得到什么好处。如果任务管理被封装到一个DLL中,我一点都不关心它的实现,并且它的实现很可能经常变动,但接口很明确很稳定,此时采用封装还差不多,最大的好处就是当DLL修正缺陷后,我可以随时简单替换一下程序,无须重新编译客户程序就可以使用它了。

在封装之前一定要先确认真的需要封装吗,封装后好处在哪儿,封装带来的收益是否大于因为封装带来的成本?我们都需要仔细权衡!

 

封装层就成为设计需要遵循的接口。

 

 

8.4 发现变化并将其封装

怎么才能发现变化?如何防止过度设计?我始终还是未得其解!

 

8.5 共性和可变性分析与抽象类

Jim Coplien有关共性和可变性分析的工作,告诉了我们如何在问题领域中找到不同变化,如何找到不同领域中的共同点:找到变化的地点,称为“共性分析”;然后找出如何变化,称为“变性分析”。

按照Coplien的说法:“共性分析就是寻找一些共同的要素,它们能够帮助我们理解系列成员的共同之处在哪里。”

可变性分析揭示了系统成员之间的不同。可变性只有在给定了共性之后才有意义:

也就是说我们在分析的时候,一定要先分析共性,再分析变性。

共性分析寻找的是不可能随时间而改变的结构,而可变性分析则要找到可能变化的结构。可变性分析只在相关联的共性分析定义的上下文中才有意义......从架构的视角来看,共性分析为架构提供长效的要素,而可变性分析则促进它适应实际使用所需。

也就是说,如果变化是问题领域中各个特定的具体情况,共性就定义了问题领域中将这些情况联系起来的概念。共通的概念用抽象类表示。可变性分析所发现的变化将通过具体类(也就是从抽象类派生而来的具有特定实现的类)实现。

......

从图8-5中可以看到,共性分析与问题领域的概念视角是互相关联的,而可变性分析与(特定情况的)实现是互相关联的。

这句话解决了我以前在做DMR时遇到的一个无法决断的问题,好几个业务对应的视图窗口都差不多,上面是一个工具条,下面是一个列表,但工具条上的操作是不一样的,有可能没有工具条,如果不采用继承,可能会出现较多的重复代码,所以当时我就采用了继承,但是后面有变化,继承关系没了,让我改动很大,当时就在考虑这种情况是不是该用继承?

从上面的话来看,如果是继承,那么也就是说,这些视图应该有共同的概念关联关系,但有吗?没有,只是从实现上可能有共性,而实现的共性并不能作为真正的共性,正是因为概念上没有共性,才导致后来的变化成为可能,才导致这些变化让我无所适从(PC中我之前嚷着要搞个继承,因为不好搞,可能说明确实没有继承关系,就跟DMR中一样,幸好当时没搞,搞了现在说不定出同样的问题了)。而PC中图形区的每个窗口,从概念上来说显然都是有共性的,比如图形区窗口,所以,我们用继承实现以后不会出现任何问题。

结论:只要概念上没有共性,就算可能出现重复代码(即实现可能有共性),千万不要采用继承,而是应该通过其他办法解决重复代码问题。换句话说,继承是用来解决概念上的必然共性,而不是实现上的可能共性。

 

 

第9章 Strategy模式

暂略

 

第10章 Bridge模式

暂略

 

第11章 Abstract Factory模式

暂略

 

第12章 专家设计之道

12.1 概览

在着手设计时,应该怎么开始呢?先获取各种细节,再看它们如何组合在一起?还是先从总体概念开始,再将其逐步分解?或者其他方式?

Christopher Alexander的方法是先把注意力放在高层的关系上--某种意义上也就是自顶向下。他认为在做出所有决策之前,理解所要解决的问题的背景是至关重要的。他用模式来定义这些关系。但是,他不仅仅提出了一组模式,还给出了一整套设计方法。他所撰述的领域是建筑学,针对的问题是设计供人们居住和工作的场所,但他的原则也适用于软件设计。

 

12.2 添加特征的创建

他的《建筑的永恒之道》不仅讲述模式,而且讲述这些模式如何协作。

一本讲述建筑设计和城镇规划的书怎么会对软件设计产生如此深远的影响呢?我相信,这是因为它描述了一种范型,一种Alexander认为设计师都应该遵循的范型。任何设计师皆然。我所感兴趣的正是这种设计范型。

 

我们先来看看Alexander是怎样教导我们的。

设计常常被认为是一种合成过程,一种将事物放在一起的过程,一种组合过程。按照这种观点,整体是由部分组合而成的。先有部分,然后才有整体的形式。

从部分到整体、从已知的具体事物开始设计,这很自然。

当我第一次读到书中这段话时,我想:“是啊。这与我观察事物的方式非常类似,先确定需要些什么,然后将它们组合起来。”也就是说,我先找出类,然后观察他们如何协作。组合这些部分之后,可以回头来看看他们是否适合总体概念。然而,即使在将注意力从局部转到全局时,整个过程中我考虑的仍然是这些部分。

对于面向对象开发人员而言,这些部分就是对象和类。我找出对象和类,为它们定义行为和接口。但由于我是从这些部分开始的,注意力也总是放在这些部分上。

但是,只是把预先形成的部分加起来,不可能形成有自然特征的任何东西。Alexander认为,从部分构造看并非好的设计方式。

一开始我对Alexander关于模块化的论述并不理解。按道理要实现重用,应该必须给出一些通用的例程,但是他却在说不要这样做。......他的意思是,如果在形成总体概念之前就开始构建模块,这些模块不可能有能力处理特殊需要。

似乎在Alexander的方法和重用目标之间我们陷入了进退两难的境地。不用担心,本书最后会解决这一难题。我希望对Alexander论述的理解能够像当初对我自己那样对你有帮助。遵循了Alexander的方法之后,我发现自己创建的类比通用部分出发所得到的更好,而且重用性更强。

Alexander所描述得失这样一种设计思想方式,开始用最简单的术语来考虑问题(概念性层次),然后添加更多特性,在此过程中设计将越来越复杂,因为我们加入了越来越多的信息。

Alexander断言:设计应该从问题的一个简单陈述开始,然后通过在这个陈述中加入信息,使它更加详细(也更加复杂)。这种信息应该采取模式的形式。对于Alexander而言,模式定义了问题域中实体之间的关系。

......

以此为基础,他还指出了优秀设计师应该遵循的规则:

每次一个--模式应该按顺序每次只运用一个。

背景优先--首先应用哪些要为其他模式创造背景的模式。

 

12.3 小结

设计常常被认为是一种合成过程,一种将事物放在一起的过程。在软件中,常用的方法是立即寻找对象、类和zujian,然后思考它们如何互相配合。

在《建筑的永恒之道》一书中,ChristopherAlexander描述了一种更好的方法,一种基于模式的方法:

1. 从对整体的概念性理解开始,以理解需要实现的目标。

2. 找到在整体中出现的模式。

3. 从为其他模式创造背景的那些模式开始。

4. 从背景向内:应用这些模式,找到新模式,并重复。

5. 最后,通过每次应用一个模式,改进设计,并在所创建的背景中予以实现。

作为一个软件开发人员,你可能无法直接应用Alexander的模式语言方法。但是,通过在已经出现的概念的背景中添加新概念进行设计,肯定是我们每个人都可以做到的。学习本书后面的新模式时,请牢记这一点。许多模式都能够创建健壮的软件,因为它们定义实现类能够在其中工作的背景。

 

第13章 用模式解决CAD/CAM问题

不理解,竟然从模式开始,略

 

第14章 设计模式的原则与策略

14.1 概览

前面已经讲述了怎样在局部和全局层次使用设计模式。在局部层次,模式告诉我们如何解决给定背景下的特定问题。在全局层次,模式提供了一张应用程序各组件的关系图。

学习设计模式的方式之一是理解如何在两个层次更有效地运用设计模式。它们将帮助你根据别人的经验开发质量更高、可维护性更好的代码。

学习设计模式的方式之二是,理解其本质机制和它们背后的原则与策略。理解这些知识将提高你的分析和设计能力。即使在还没有发现设计模式的情况下,你也能知道应该怎么做,因为你已经知道了按模式的方式解决这个问题所需的基本概念。实际上,这是在努力理解前人在发现后来成为模式的解决方案时所用的思路。

本章将:

叙述开闭原则,这是众多设计模式的基础。

讨论从背景设计原则,这是Alexander模式的目标。在此过程中,本章还将讲述依赖倒置和Liskov替换原则,这是设计模式的两个更深层次的基础。

讨论封装变化原则。

讨论抽象类和接口之间的差异。

最后讨论需要对模式保持一种理性的怀疑态度。因为它们都是有益的指导,但并非颠扑不破的“真理”。

 

14.2 开闭原则

软件显然是需要具有扩展性。然而,修改软件会面临引入问题的风险。这种两难局面是BertrandMeyer提出了开闭原则(open-closed principle,OCP)。这一原则可以这样理解:模块、方法和类应该对扩展开放,对修改封闭。也就是说,应该将软件设计得不对其修改就能扩展功能。

初听起来这似乎不近情理,但实际上我们已经见过遵守这一原则的例子。例如,在Bridge模式中就很有可能不修改任何已有的类而增加新的实现(也就是扩展该软件)。开闭原则本质上意味着将软件设计成为新功能能够作为单独的模式加入系统,这样就尽量降低了集成的成本。

完全遵守开闭原则几乎是不可能的,到那时它作为一个目标,指引正确的方向。代码越遵守这一原则,以后适应新(而且可能是无法预测的)需求就越轻松。

 

14.3 从背景设计原则

Alexander教导我们从背景设计,在设计各部分所呈现的细节之前先创建总体概念(bigpicture)。大多数设计模式都遵循这一方法,而有些设计模式相对其他模式遵循这一方法的程度更深。到现在为止已经讲述的4各模式中,Bridge模式是遵循这一方法的最好例子。

请参考第10章中的Bridge模式图(见图10-3)。在决定如何设计Implementation类的接口时,需要考虑其背景:从Abstraction派生的类如何将使用这些Implementation类。

例如,如果所编写的系统需要在不同类型的硬件上绘制各种几何形状,因此需要不同的实现,我就会使用Bridge模式。Bridgemoshi建议:几何形状将通过一个公共接口使用该实现部分(也就是也编写的绘制程序)。像Alexander教导我们的那样从背景设计,也就意味着我应该首先理解几乎形状的需求,即要绘制什么?这些形状将决定实现所需的行为。例如,实现(绘制程序)可能必须绘制直线、圆等。换言之,即使具体的抽象(不同的几何形状)依赖于具体的实现(绘制程序),让然是形状定义绘制程序。应该从面向服务开始,问一问:“我们要提供哪些种类的服务?”因为是从Abstraction类(及其派生类)定义,所以必须在抽象层次定义服务。在为实现开发接口时,我要考虑具体抽象中所说明的概念的需求,而不仅仅是所面对的特殊情况。

这就是所谓“依赖倒置原则”(dependency inversion principle,DIP):

高层模块不应该依赖于底层模块。高层模块和底层模块都应该依赖于抽象。

抽象不应该依赖于细节。细节应该依赖于抽象。

Christopher Alexander将此称为“复杂化”--一种从最简单(概念性)的层次开始,然后逐渐添加细节和特征,随着逐步深化,设计也渐趋复杂的过程。复杂化和依赖倒置是使用设计模式的中心基础原则。

这一原则隐含这使用对象和被使用对象之间只能在概念层次存在耦合,而非实现层次,这与《设计模式》一书中建议的应该“按接口设计”可以说是英雄所见略同。在这个层次考虑新的服务和新的库户对象的关系,比面对如何提供服务的细节知识时考虑得要容易得多。

我将花一些时间找出一般情况和可能遇到的各种变化。这需要考虑类所处的背景,并使用一种称为共性和可变性分析的技术。该技术将在第15章中详细讲述。这能够帮助我根据进一步一般化所需的成本,决定实现的一般化程度。这往往能比其他思考方式带来更具一般化的实现,所增加的成本很小。

例如,在考虑绘制几何形状时,我可能很容易就找出直线和圆是需求。如果我自问:“有了直线和圆之后,什么形状还无法支持?”我可能会注意到还不能实现椭圆。现在可以这样选择:

除了直线和圆,再实现一种绘制椭圆的方法。

认识到椭圆是圆的一种一般情况,改而实现椭圆。

如何成本大于预期的收益,就放弃实现椭圆。

设计在所处的背景中外部行为相同的多个类时,这一点非常重要。这能够将客户类与这些实现解耦,还可以如设计模式所建议的提升类型的封装性。BarbaraLiskov于1998年提出了一个原则,复述如下:

一个从基类派生的类应该支持基类的所有行为。

只要可能,我喜欢将这一原则扩展为:让使用对象甚至无法知道是否存在派生类。也就是说,对于给定的基类(或者接口)的引用,使用对象无法知道其是否存在派生类(或者实现类)。因此所有这样的派生类(或者实现类)都是彼此可以互换的,从而对类型进行了很好的封装。实践中这意味着子类型不应该在基类型的公开接口中添加新的公开方法。这还意味着基类型必须是所建模的概念的完整规格说明。

前面的例子说明了另一重要的设计概念:

只是可能存在并不意味着必须要实现。我的设计模式经验表明,设计模式赋予了我对问题领域的洞察力,但是,并非总是(甚至往往不需要)真的根据这种认识去做,为还没有出现的情况编写代码。当然,这些模式有助于从背景设计,他们使我能够预测到可能的变化,因为已经有助于看到什么地方可能出现变化,而不是会出现哪个具体的变化。我用来封装当前变化的具有明确定义的接口经常也会限制新的需求带来的影响。

Abstract Factory模式是另一个从背景设计的好例子。开始可能只知道可以用某种工厂对象协调一系列(或一组)对象的实例化,但是,现在明白还有它许多不同的实现方式:

使用派生类 经典的Abstract Factory模式实现建议为需要的每组对象实现一个派生类。这有些笨拙,但也有其优点,可以不改变任何已有类而增加新类

使用哈有switch语句的一个对象 如果希望按需要修改AbstractFactory类,可以只让一个对象包含所有这些规则。尽管这并不遵循开闭原则,但是它在一个地方包含了所有的规则,不难维护

使用含有switch语句的配置文件 这比前一种更加灵活,但有时仍然需要修改代码

使用含有动态类加载的配置文件 动态类加载是一种根据字符串中的对象名实例化对象的方法。这种实现灵活性很强,可以不修改任何代码增加新的类和新的组合

对所有这些选择,应该怎样决定用哪种方案实现AbstractFactory呢?根据模式出现的背景来决定。根据一下因素的不同,这四种方式各自都有其优于其他方式之处:

未来变化的可能性。

不修改当前系统的重要性。

谁控制将要创建的对象集合(我们还是另一个开发组)。

使用何种编程语言(比如:C++,C#,Java)。

是否有数据库文件或配置文件。

这个列表当然并不完整,前面的实现方式列表也是如此。但是有一个点应该很清楚了:在不懂得怎样使用AbstractFactory模式(也就是说不理解其背景)之前要想决定其怎样实现,显然是徒劳无功。

 

怎样进行设计决策

尝试在不同实现方式进行选择时,许多开发人员会这样问:“这些实现方式中哪个更好?”这并不明智。问题在于,往往没有哪个实现方式天生优于另一个。应该这样问,对于每种实现方式,“什么情况下它优于其他方式”然后再问:“哪种情况与我的问题领域最相似?”这种反复思考的工作量并不大。这种思考方法能够使我更明了问题领域中的变化和可伸缩性问题,而且不会在得到第一个、可能不完整的答案之前就停下来。

 

Adaper模式也说明了背景设计原则,因为它几乎总是出现在某个背景中。根据定义,Adaper类的作用是将一个原有接口转换到另一个接口。有一个问题其答案显而易见:“我怎么知道应该将原有接口转换成什么样呢?”在背景(也就是要适配的类或抽象)显现之前,通常都不会知道。

......

Facade模式与Adaper模式就背景而言非常相似。一般Facade模式是在其他模式或类的背景中定义的,也就是说,必须等到知道谁想用Facade模式设计自己的接口。事实上,Facade的接口经常是随着使用它的系统部分不断开发出来而逐步开发出来的,开发期间每个新的系统部分都为Facade建立了更加丰富的背景。

在刚刚开始使用模式时,我总是认为自己可以发现哪些模式在为其他模式创造背景。在Alexander的PatternLanguage一书中,他对于建筑模式就能够这样。因为很多人都在谈论软件的模式语言,我想:“我为什么不行呢?”似乎很显然,Adaper模式和Facade模式总是在其他模式所创造的背景中定义的。对吗?

错。

同时负责教授别人的软件开发人员有一个好处,就是有机会比纯粹的开发人员接触更多的项目。在刚开始教授设计模式时,我认为Adaper模式和Facade模式总是在其他模式定义背景的非创建型模式之后出现。事实上确实经常如此。但是,有些系统需要构造一个特定的接口。这时,Facade模式或Adaper模式就可能成为最高模式。

 

14.4 封装变化原则

有些读者可能注意到我的所有设计中都有一个相似之处:继承层次中类很少超过两层。那些更次更多的设计往往都是因为有的设计模式的结构要求有两层作为派生类的基础。(第17章中讨论的Decorator模式是一个使用了三层的例子)。如果我出现了多层继承的,几乎总是为了要消除某种冗余(当然,对于Decorator模式,Decorator抽象类的存在是为了在一个地方存放装饰与被装饰物的关系。)。

之所以这样,是因为我的设计目标之一就是不让一个类封装两个要变化的事物,除非这些变化明确地耦合在一起(比如,一个数据库的多个实现方法)。这样会降低内聚性,变化之间的耦合也无法松散。迄今为止所讲述的模式已经说明了有效地封装变化的各种不同方法。

Bridge模式是封装变化的一个绝佳范例。Bridge模式中的实现除了能够通过一个公共接口访问之外完全不同。新的实现部分可以通过在此接口中实现相应功能系统加入。

Abstract Factory模式封装的变化是哪些系列或哪组对象可以实例化。这个模式的实现有很多种方式。需要指出的是,即使一开始选择了某种方式,然后发现另一种方式更好,可以在不影响系统其他部分的情况下改变其实现(因为工厂对象的接口不变,只是其实现方式改变)。因此,AbstractFactory模式概念本身(按一个接口实现)隐藏了如何创建对象上的所有变化。

Adapter模式可以用来给截然不同的对象定义一个公共接口。现在我经常要用到它,因为其他许多模式都要求按接口设计。

Facade一般不封装变化,但是,我见过很多情况下要用Facade模式处理一个特定的子系统。然后,当另一个子系统出现时,新子系统的Facade也要按相同的接口创建。这个新类结合了Facade模式和Adapter模式,因为主要动机就是简化,但现在还有一个附加条件:要与前面已经使用的接口保持一致,从而保证客户对象都无需修改。这样使用Facade模式就隐藏了所有子系统上的变化。

但是,模式并非只能封装变化。它们还有助于找到对象之间的关系,帮助我们思考问题域中的关键概念。还是来看Bridge模式,注意到了吗,这个模式不仅定义和封装了抽象和实现中的变化,而且还定义了两组变化之间的关系。

 

14.5 抽象类与接口

抽象类和接口之间的一个区别,就是抽象类允许有公共的状态和行为。也就是说,如果所有派生类都有一些公共的状态或者行为,就可以放在抽象类中。对于Java和C#这样的语言而言,进行这种区分尤其重要,因为这些语言只允许从一个类继承。换言之,在不需要的时候不要使用抽象类,因为只有从一个类派生的机会。

对于这两种获得多态性的机制之间的区别,还有另外一种思考方式对设计人员更有帮助。他们的区别在于本章前面提到的各原则的背景上。抽象类可以看成是一种聚集相关实体的方式。其关注点是如何设计这些具体的实体(派生类),从而可以以同样的方式使用它们。也就是说,如何保持这些实现并封装起来。此处的关注点仍然遵循着依赖倒置原则:考虑服务对象(实现),看如何抽象它们,使用对象才不会与特定于实现的细节相耦合。

而为了这样使用接口,在设计时应该问的是:“这些东西如果要以相同的方式使用,必须都有什么样的公共接口?”

但是还必须考虑另一方面,接口的关注点是要使用这些派生/实现的对象。也就是说,服务对象应该有什么样的接口,才能最好地服务于背景/控制对象?在前面的电子商务的例子中,问题就变成了:“计税对象应该有什么样的接口,才能最好地服务于SalesOrder对象?”这在依赖倒置原则的基础山更进了一步。

从客户对象开始,可以将“被使用的”对象分化为更小、内聚性更强的部分。换言之,如果使用对象要求许多不同类型的对象(即多个抽象),可以为每种类型设置一个接口。而结果将是比其他方式更简洁的接口。

不能仅仅因为接口看上去跟符合依赖倒置原则,或者接口更简洁,就认为接口优于抽象类。

按照这种思路,你很可能在开发了一个接口之后,却发现应该使用抽象类,因为已经定义的对象有一些公共的状态/行为,如果将它们放在实现类中就会出现冗余。

也可能设计一个接口,然后用一个抽象类实现该接口。

抽象类能够确定默认行为,从而使实现类更加简单,维护起来也更容易。

这样,具有公共状态或行为的对象从这个抽象类派生,而不直接共享这一状态或者行为的对象(或者优于其他原因必须从另一个类派生的对象)实现接口。

 

14.6 理性怀疑原则

基于模式的分析方法已经用于许多学科。在作者本人从事的人类学和知识工程学领域,分析人员已经获得了许多可以用模式表达的教益。模式本身是非常有用的,但是应该将它们用作一种思考问题的辅助手段,而不是解决问题的处方。这一点无论怎么强调都不过分。

那些概念层次的模式和模型都不是真理,它们只是真理的抽象。它们是以往经验和教训的结晶,应用于真实世界必须具体问题具体分析。在使用模式时,有如下常见错误:

浮于表面 仅仅对底层情况有了一些肤浅的理解,就草草选择一个模式

偏见 对模式过于偏信。根据已经选定的模式/模型来解释所有数据,不愿意对自己的偏见有任何质疑

错选 不理解模式好适用的背景和条件(对各模式的分类关系理解不全),选择了错误的模式。

误判 不熟悉各种模式,因为无知而导致误判

削足适履 忽略了实际的、具体实例行为中的例外情况 ,因为它们似乎不符合模式中所表达的理论。很可能会使所建模出来的对象过于僵硬,不符合实际情况

请记住模式都是发现而不是发明出来的,这一点很重要。“适合”某个问题领域的模式就在问题之中,而不是强加在问题之上。与此类似,模式实现的具体方式应该由问题的本质、约束条件和需求等等决定,而不是根据在某本模式书中碰巧看到的某个实现。

 

第15章 共性与可变性分析

15.1 概览

本章说明如何使用共性和可变性分析(commonality andvariability analysis,CVA)开发高层的应用程序设计。虽然设计模式并不能用于所有设计之中,但是他们提供的教益是普适的。这些教益中最重要的一条就是可以使用CVA找到系统中的变化。然后就可以按照设计模式的其他教益(按接口编程、使用聚集封装变化)获得灵活和易于测试的设计。

 

15.2 共性和可变性分析与应用程序设计

......

我建议,设计程序时应该按照这样的方式进行:首先,使用CVA找到问题领域中存在的各种概念(共性)和具体的实现(可变性)。这时我们最感兴趣的是找到其中的概念,但是这一过程中也会发现许多可变性。问题领域中任何没有包含在这些概念中实体(比如可能有一些属于“某种”对象的对象)也应该找出来。然后,在所需功能的概念都找到之后,继续封装这些概念的抽象制定接口。接着考虑你将如何使用从该抽象派生的具体实现,根据这一点派生接口。

 

 

第16章 分析矩阵

16.1 概览

本章将继续前面第9章中开始的电子商务案例研究的讨论。

现在我们已经讨论了许多模式,应该回过头来,考察软件开发中最大问题之一:处理问题域中的变化。设计模式可以帮助分析师成功地找到变化并很好地将其组织起来。

本章将:

思考现实世界中的变化问题。

考察电子商务案例研究中代表着问题主要变化的部分。在解决这一问题的过程中,形成一个分析矩阵,它是决策表的一种简单变形,我发现它对于理解和协调概念中的变化很有帮助。分析矩阵和ChristopherAlexander、Jim Coplien的概念有异曲同工之妙。

描述如何z爱实际中使用分析矩阵。

 

 

关于客户的一点说明

与客户打交道的经验使我懂得以下几点:

他们通常非常了解他们的问题域(大多数我们永远赶不上)。

一般情况下,他们不会像开发人员经常的那样在概念层次表达事情。相反,他们会谈得非常具体。

他们经常用“总是”表达“通常”。

他们经常用“从不”表达“很少”。

总之,对于非常具体的问题,客户详细的回答一般是可信的,但是他们一般性的回答却不可信。我尝试在非常具体的层次与他们沟通。即使是那些听上去似乎是在概念层次考虑问题的客户,往往并非真的如此,只是想努力帮助我而已。

 

第17章 Decorator模式

 

第18章 Observer模式

 

第19章 Template Method模式

 

第20章 来自设计模式的教益:各种工厂模式

20.1 概览

本章讨论工厂(创建其他对象的对象)的使用。虽然《设计模式》一书中介绍了工厂,但是它们在设计中的使用书中并没有完整地予以讨论。

本章将:

讨论为什么使用工厂会极大地简化设计和代码。

阐述正确的步骤:先了解对象,然后再决定如何创建/或管理它们。

 

第21章 Singleton模式和Double-CheckedLocking模式

 

第22章 Object Pool模式

22.1 概览

......这个项目证明了我在第10章中提出的一个观点,即如果你理解了模式的原则,遇到可以应用未知模式的项目时,自己都有可能将模式推演出来。具体到这个项目,我最终从设计模式中推出了ObjectPool模式。......

 

第23章 Factory Method模式

 

第24章 工厂模式的总结

24.1 概览

本章将总结本部分中我们所得到的教益。还会讨论创建和使用对象的三个主要任务。

1. 根据所履行的责任找出对象。这时共性和可变性分析极为有用。

2. 决定如何使用这些对象。主要是考虑对象间的关系,这正是许多模式所要解决的问题。

3. 决定如何管理这些对象。这正是工厂的用武之地。

还将总结将软件开发任务分为几个不同步骤,每一步骤只解决以上一个问题的有点。

 

24.2 软件开发过程中的步骤

长期以来,人们已经公认,开发软件时将代码分为多个模块对于提高所生成的代码的质量有极大的好处。模块更容易管理,而且在设计得当时也更容易修改或扩展。但是,在功能分解的系统中,模块主要用于模块化不同的功能。而在面向对象系统中,出现了一种新的可能。第1章讨论了三种不同的视角:概念视角、规约视角和实现视角。本章讨论设计应用程序时可以使用的另外一组视角:使用视角和创建/管理视角。

这样做的威力来自这样的事实:概念上相似的对象从使用的视角来看,可以以同样的方式处理。然而,创建对象时,负责创建的实体通常需要了解要创建的是哪个具体的对象,以及何时创建这个而非那个对象的规则。在设计系统时,让最复杂的部分在概念层次使用其他对象是最有用的。这意味着要遵循开闭原则、依赖倒置原则和Liskov替换原则。但是,要实现所有这些,使用对象就不应该知道所用的是哪一个特选对象,因此,需要有对象来负责此事--工厂就出现了。

称工厂为“对象管理者”可能更适合,因为创建对象只是它们的责任之一。通过封装对象的创建和其后的管理,更复杂的使用对象就无需处理这些问题了。这意味着出现新功能的需要时,可以已有系统的背景下处理,开发出新功能。如果概念上还没有它的位置,那就在系统中重构出一个,然后添加新代码。

这是一个两步方法。首先,为新代码重构一个位置,然后将其加入系统。这种方法的好处有以下方面:

这意味着我们总是有能够工作的系统。

小步前进意味着工作减少了。

集成新代码的成本(大多数人都认为这是扩展代码成本最高的地方)始终很低。

代码的质量不会降低,而且设计不会不坏。

 

24.3 工厂与极限编程实践殊途同归

重构可以用来修改劣质代码或者作为扩展优秀代码的方式。使用重构来添加新功能的正确过程如下:

重构已有代码,以配合新代码。(没有增加任何新功能)也就是说,对于关注区域中不符合开闭原则的代码,要重构使其符合。

然后加入新功能。

在工厂背景下,这意味着要这样的编写系统:使用对象不知道它在使用哪个特定的实现。如果什么地方不符合,而且完成该功能的方法不止一种,那就重构代码达到这一点。完成以后,添加新功能就只需要编写出来(这往往避免不了的),修改负责这些类型对象的工厂/管理对象。

 

24.4 系统的扩展性

这种方法的优点在于,它对于各种尺度的系统都行之有效。在开始,我们的选择很少(如果说还有的话),工厂可能是类自己的封装了构造操作的方法。接下来,随着系统越来越复杂,我们可能编写专门的工厂/管理对象,在其中写入正在讨论的规则。最终,可能需要使用数据库或者配置表现体现规则。

无论怎样完成,工厂/g管理逻辑都封装在方法和对象之后的,从而将这些规则与使用方软件分离。如果逻辑变得非常复杂(这是常事),它与系统仍然是松散耦合的,可以保持灵活性和易扩展性。

软件中总是会发生变化。无论何种变化都或者会影响对象、服务的用户,或者影响实例化对象的工厂。保持分离,就减少了维护工作量,只需要维护这些实体中的某一个,很少会是两者。

这又归结到一个基本原则,对于系统中的任意两个实体A和B,应该将它们之间关系限制为A使用B,或A创建/管理B,但是两种关系永远不要同时存在。

 

第25章 设计模式回顾:总结与新起点

25.1 概览

在任何书的最后,回顾一下所得总是很好的。本书中,我们尝试通过一种可能非常新颖的方法,使你更好地理解面向对象原则。其中教授了设计模式,并使用设计模式说明设计模式是怎样解读面向对象范型的。设计模式回答了一个基本问题:“为什么以这种方式设计?”

在本章将回顾以下内容:

看待面向对象原则的新视角,以对设计模式的理解为基础。

设计模式如何帮助我们封装实现。

共性和可变性分析,如何与设计模式一起帮助我们理解抽象类。

按照所涉及的责任对问题域进行分解。

设计模式与背景设计。

模式之间的关系。

设计模式与敏捷编程实践。

最后,我提供了一些来自亲身经验的实践注记。

 

25.2 面向对象原则的总结

在讨论设计模式的过程中,我们已经说到了许多面向对象范型的原则。这些原则可以总结如下:

对象是具有明确定义的责任的事物。

对象对自己负责。

封装指的是任何形式的隐藏:数据隐藏 实现隐藏 类隐藏(在抽象类和接口后)设计隐藏 实例化隐藏

使用共性和可变性分析抽象出行为和数据中的变化。

按接口设计。

将继承看成一种将变化概念化的方法,而不是创建已有对象的特殊情形。

将变化放入一个类中,并与该类中的其他变化解耦。

力求送耦合。

力求强内聚。

将使用一个对象的代码与创建该对象的代码分离。

在应用“一次且仅一次”规则时要绝对小心。

通过“按意图编程”,使用反映意图的名字,确保代码的可读性。

在编程之前就考虑代码的可测性。

 

25.3 设计模式如何封装实现

......

隐藏实现的价值在于,模式使开发人员能够容易地添加新的实现,因为客户对象不知道当前实现的具体工作细节。

 

25.4 共性和可变性分析与设计模式

......但是,更重要的一点是寻找共性(使用共性和可变性分析)本身有助于发现问题域中存在的模式。

......

Stragetry模式与此类似,看到若干不同的规则时,我就知道应该寻找这些规则之间的共性,从而将它们封装起来。

虽然我们只用共性和可变性分析就可以推出许多模式,但是不断学习模式、阅读相关文献仍然非常重要。模式为讨论分析和设计中所得教益提供了背景;模式为开发团队提供了讨论问题的通用词汇表;模式使我们能够将最佳实践方法应用于代码之中。

 

25.5 按责任分解问题域

......

将这种方法扩展一下,就是前面讲过的一条规则:设计师不应该在了解所需的所有对象之前操心对象的实例化。这一规则可以看成是将问题域分解成两部分:

需要哪些对象

如何实例化和管理这些对象

各种具体的模式往往有助于思考如何分解责任。例如,在需要将问题域分解成总是要用的主要责任(即ConcreteComponent)和可能会有的变化(即Decorator)时,Decorator模式为我们提供了一种灵活地组合对象的方法。Stragegy模式则将问题分解成使用规则(无论使用什么规则)的对象和规则本身。

 

25.6 模式和从背景设计

......

事实上,从一般意义上说,按接口设计和多态就是一种从背景设计。如图25-1,这是图8-5的复制。请zhyi抽象类的接口定义了背景,所有派生类都必须在此背景中实现。

 

25.7 模式内部的关联

需要老实交代的是,我曾经在设计模式课堂上用Alexander的某些话来开玩笑。在用了大半天谈论模式有如何如何好之后,我拿起Alexander的TimelessWay of Building一书,翻到最后,说:

这本书有549页。在第545页,显然,非常接近结尾了,Alexander这样说:“在这最后阶段,模式不再重要......”

......

模式为我们提供了谈论这些内容的方法,但是,模式本身并不是最重要d饿。软件模式也是如此。

......

 

25.8 设计模式与敏捷编程实践

......

我们已经说明了模式能够帮助引导我们进行重构。我们还讨论了模式与代码的可测性的内在一致性。良好的编程和设计技术往往都不会相互抵触。即使乍看上去有些矛盾,稍加深入研究,就会发现它们都是殊途而同归。

 

25.9 实践注记

从一本书中完全学会模式是不可能的。必须编写代码,在设计中使用它们。将模式编码实现是很容易的。假想出一个示例,实现模式的设计是很好的第一步。但是,请记住,这并非真正的模式,而只是它的一个方面而已。当然,它有助于你理解其他方面。

在学习模式的过程中,寻找以下约束因素和概念会有所帮助:

这个模式隐藏了什么实现?这样我们就可以修改它。

这个模式中有什么共性?这有助于你找到共性。

这个模式中对象的责任是什么?这将提供这些对象的约束因素的信息。

这个模式本身怎样成为从背景设计的微观示例?这使我们能够更好地理解为什么这个模式是优秀设计。

 

开发软件时,看看是否这些问题的答案与正在解决的问题域有关。如果有关,看看模式中是否有什么可以派上用场。

 

第26章 参考书目

26.2 推荐阅读

The Timeles Way of Building

无论从个人还是专业而言,本书都是我的最爱。这本书不仅非常有趣,而且充满真知灼见。如果准备只阅读本列表中的一本书,请读这一本。

 

26.8 个人推荐

我相信最好的设计师绝不是那些生活只知道编程的人。相反,能够思考和倾听、具备更完善而深沉的性格、富有思想,才是成为一名优秀设计师所需的。你能够与其他人更好地沟通。你能够从其他学科获得思想(就像我们从建筑学和人类学中获取模式思想那样)。你将创建更加以人为本的系统,毕竟,我们的系统是为人而存在的。

我的许多学生问我喜欢读些什么书,哪些书影响了我的思考方式,哪些书在我的人生旅途中曾经给我以帮助。以下是我的推荐书目。

The Blue Day of Book 这是一本有趣、令人愉悦的书。情绪低落时,请读这本书。

Think and Grow Rich “富有”不仅意味着金钱--它意味着各种形式的富有。这本书对我个人和事业的成功都有深远的影响。

Stopping:How to Be Still When YouHave to Keep Going 这本书提醒那些工作狂,如何减慢节奏,享受生活,同时仍然完成所有工作。

The Greatest Salesman in theWorld

Unlimited Wealth

My Grandfather's Blessing

The Mind Map Book

How the Irish Saved Civilization

Religion and Rise of WesternCulture

The E-Myth Revisited

Simplicity: The New CompetitiveAdvantage in a World of More,Better,Faster

Transforming Culture

The Ethnographic Interview

Knowledge Management Methods

 

 

complement vt.弥补

Distilled adj.由蒸馏得来的

demify vt.使非神秘化

idea(思想)

origins(起源)

discipline(学科)

Commonality(共性)

hold(抱着)

bandy vt.传播

to become proficient with  变得对...精通

esoteric adj.深奥

literature(著作)

equipped(被装备)

reasonably 相当地

I was convinced(深信) that design patterns were thegreatest thing to happen to(发生在) software design since the invention ofobject-oriented design.

figure vt.认为

I was no better off.没有得到改善

frustrated 失败的

rather than(不是)

Patterns are supposed to be sewn(缝合) together tosolve a problem.

Timeless adj.永恒的

amazing(令人惊异的)

implementation(实现)

in trying to(企图)

stitch(缝合)

grounding n.基础

embark vt.着手

A slight(轻微的) digression(离题,脱轨).

guiding(指导性) principles

stated(说明)

Gang(帮)

relate vt.叙述

Artificial Intelligence(人工智能)

codify vt.系统化

I was hooked.我上瘾了。

Rationale n.基本原理

introductory(介绍性的)

get up to(达到)

scholarly(精深的)

We are trying give you a taste(体验) for designpatterns, to expose you to the breadth(广度) of

covet  vt.渴望

form(表格)

follow(遵循)

requirement(需求)

latest(最新的)

understanding(理解)

grow out of 由...而来

scourge n.灾难

analyst(分析者)

virtually(实际上)

Verify this assertion(命题) for yourself. 你自己验证这个命题。

accommodate(适应)

Modularity(模块化)definitely(无疑的)helps to(有助于)

variation(变化)

Cohesion(内聚)

Coupling(耦合)

devil(罪魁祸首)

Let's say that ... 假如...

attend(出席)

No one other than you(除了你没人)

shift(转移)category(种类)

perspectives(透视图)

concepts(概念)

a conceptual model should be drawn(描绘)

specific(明确的)

consistent(一致的)

think about(考虑)

great power(强大)

constantly(经常地)

Bottom line 要点

Special(特殊的)

overview(概述)

modeling(建模) language

community(社区)

diagrams(图表)

Interaction(交互) Diagram

diagrammatic(图表的)

come for(用来...)

still(还有的)

analysis phase(阶段)

Activity(行为)

deployment(展开)

collection(集合)

represent(描绘)

spare adj.备用的

Sequence Diagram(序列图)

flesh out(使...生动)

Go for(努力获取)

clarity(清楚)

communicate(传达)

Part Overview

feed vt.满足

cry out for 迫切需要

orchestrate vt.使...和谐地结合起来

sheet n.薄片

cutout n.剪切块

the problem domain 问题域,也就是现实中真实情况

router n.刳刨机

Numerically controlled (NC) machine.数控机

Messy adj.凌乱的

pitfall n.缺陷

dive into(钻进)

necessarily(adv.不一定)

objective(n.目标)

Gut instinct(内心直觉)

special-casing(特例化)

explosion(n.激增)

overreliance(n.过度信赖)

pertinent to 和...有关的

espouse vt.支持,赞成

semina adj.创造性的

cutting adj.剪切下的

abound vi.大量存在

beholder n.旁观者

architectural adj.建筑的

objective basis 客观的原则

consensus n.多数人的意见

postulate vt.假定

cultural anthropology 人类文化学

transcend vt.超越, 胜过

proposition(n.主张)

objectively(客观地)

go about(着手做)

spring from(从...冒出来)

in common with ... 和...一致

Excerpt n.摘录

To review(回顾一下), Alexander says that a descriptionof a pattern involves four items:

For now(现在)

claim(主张)

specialized(特殊的)

Facade(n.正面)

I relate(使...联系) the Facade pattern to the CAD/CAMproblem.

intent(n.目的)

the loser(那些水平差的)

derivative(n.派生的事物)

preexisting(我们期望的)

step back 停下脚步

reflect on 思考

original adj.新颖的

a step up(n.提升)

preliminary(初步的)

superior(出众的)

tout v.吹捧

rephrase vt.改述

terrain n.地形

overkill n.过度的杀伤力

In effect adv.有效地

sensible(adj.切合实际的)

I derive(vt.推导出)

working through(完成)

go into(vt.探

supposed to(我被允许去)

It turns out that ...   结果...

to be honest 老实说

at this point 此时此地

If you understand the stated(adj.一定的) intent(意思),then you are that much(有那么些) ahead(超前的).

The Bridge pattern is one of the toughest(困难的,费力的)patterns to understand in part( 在某种程度上) because it is so powerful and appliesto so many situations. Also, it goes against(vt.反对) a common tendency(趋向) tohandle special cases(特例) with inheritance. However, it is also an excellent(极好的)example of following( 遵循) two of the mandates(n.训戒) of the design patterncommunity: "find what varies and encapsulate it" and "favor(更喜欢)object composition over class inheritance" (as you will see).

 

I will work through(完成) an example from scratch(从零开始).Starting with requirements(从需求开始), I will derive(推导出) the pattern and then seehow to apply it.

up front 在前面

explore for(仔细检查)

mandate n.命令,要求

It occurs to me that 它使我想到...

walk through vt.敷衍了事地做完; 走过场

linearly(线性地)

Characteristics(特征)

specialization(特例化)

come up with(提出)

endorsing(认可)

in-depth(深入的)

alternatives(可供选择的办法)

deficiencies(不足)

philosophy(哲学)

in point(adv.相关的,适用的)

vice versa(反之亦然).

allow for(考虑到,估计到)

true(真理)

Now that 既然

in a position to 能够

It is almost axiomatic 几乎是公理

restatement n.重申

Retrospect n.回顾

propose vt.推荐

examine vt.分析

in a sense(在某种意义上)

presenting(提出)

area(领域

arena(舞台)

have a handle on 理解,明白

so long 再见

tried and true adj.经考验证明好的

Design is often thought of as(被看作是)

synthesis(合成)

come(vi.产生)

natural(自然的)

pretty much 非常接近

identify(确定)

come up with 提出

thesis(理论)

Even though(即使)

practitioners(从业者)

respect(vt.尊敬)

homogeneity(同质性)

depersonalization(n.失去个性)

not at all(一点也不)

pleasing(令人高兴的)

In short(简而言之)

specific(独特的)

differentiating(异化)

complexification(复杂化)

come into being(形成)

embryo(胚胎)

What in the world(究竟)

unfolding(演变)

in the mind of(在...头脑中)

takes the form of(采取...的形状)

in terms of(根据)

inward(向内)

observation(观测结论).

ever, this also

worked against(产生反作用)

in that(由于)

espousing(赞成)

predetermined(预先确定)

evangelize(宣扬)

preset(预先设置)

manifest(表明)

improved(改良的)

experience(体验)

developed(vt.揭露)

initial objective(最初目的)

clearly(无疑地)

dilemma(n.进退两难的局面)

paraphrase(解释)

contradictory(矛盾)

somehow(adv.以某种方式)

so far(到目前为止)

case study n.案例分析

general adj.大体的

in the short run adv.从短期看

in the long run adv.最后

suboptimal adj.不最理想的

when it comes to 当提到

management n.管理人员

deliver vi.交付

over time 随着时间的过去

Presumably adv.推测

intermittent adj.间歇的

keep track of v.明了

after all adv.毕竟

dependency(附属性)

dependents(附属体)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值