第10章 设计模式

“……描述一个在我们周围一再出现的问题,然后描述解决这个问题的核心方法,这样就能够无数次地使用这个解决方法而不必重复劳动。”—Christopher Alexander

本章介绍程序设计的重要和非传统的“模式”方法。

“设计模式”(design pattern)运动或许是在面向对象设计方法学前进过程中的最新、最重要的一步,最初把设计模式概念载入编年史的,是Gamma、Helm、Johnson和Vlissides等4人合编的《Design Patterns》(Addison Wesley,1995)[1]一书,这本书一般也被称为“四人帮”(Gang of Four, GoF)书。“四人帮”针对问题的特定类型提出了23种解决方案。在本章中,讨论设计模式的基本概念并且给出一些代码示例,用于说明精选出来的设计模式。希望这样能够促使大家研读更多关于设计模式的资料,设计模式当今已经成为面向对象程序设计的几乎所有必须掌握的语汇的重要源泉[2]

10.1 模式的概念

最初,可以将模式看做解决某一类特定问题的特别巧妙和具有洞察力的方法。它体现出一支开发团队从一个问题的所有角度出发做出全面的分析后,提出的最通用最灵活的对这类问题的解决方案。这类问题也许是读者以前曾经遇到和解决过的问题,但是读者那种解决方案大概没有即将看到的在模式中体现出的完整性。此外,模式的存在独立于任何特定的实现方法,我们可以用多种方法来实现它。

虽然称为“设计模式”,它们实际上与设计领域并无联系。模式与传统的关于分析、设计和实现的思想方法有所不同。模式体现了一个程序内部完整思想,因此它也能够跨越分析阶段和高层设计阶段。然而,因为模式常常有一个直接的代码实现,所以在底层设计或编码实现之前很难将其表示出来(在进入这些阶段之前,人们可能不会认识到需要某种特定的模式)。

可以把模式的基本概念看做一般情况下程序设计的基本概念:增加一些抽象层。当人们对某事物进行抽象的时候,隔离特定的细节,最直接的动机之一是为了使变化的事物与不变的事物分离开。做到这一点的另一个方法是,一旦发现程序中的某些部分可能被修改,那么就要阻止那些修改在代码中到处传播副作用。如果做到了这一点,代码不仅比较容易阅读和理解,而且也比较容易维护—这样做会带来一个注定的结果,那就是在软件开发的全过程中降低成本。

开发一种优雅和可维护的软件设计最困难的部分,常常是发现所谓“变化向量”(the vector of change)。(在这里,“向量”应理解为自然科学中的最大梯度,而不是一个容器类。)这就意味着寻找系统中变化的最重要的事物,换句话说,去寻找系统中开发成本最高的地方。一旦找到这个“变化向量”,就可以围绕这个焦点来构建系统的设计。

因此,设计模式的目标是封装变化(encapsulate change)。如果从这点来看,在本书中,读者已经看到了一些设计模式。例如,可以把继承(inheritance)想象为一个设计模式(尽管是由编译器提供的一个实现)。它表示行为不同(这是变化的事物)的一些对象,它们具有相同的接口(这就是所谓不变的事物)。组合(composition)也可以被认为是一种模式,因为可以改变—动态或静态地改变—实现类的对象,因此而改变类的工作方式。然而正常情况下,由编程语言直接支持的特性不能被归类为设计模式。

读者也已经看到了“四人帮”书中出现的另外一个模式:迭代器(iterator)。在STL的设计中它被当做基本的工具来使用,这在本教材中较早时已经讨论过。当分步骤和依次挑选容器中的元素时,迭代器隐藏容器的特殊的实现。允许使用迭代器编写通用的代码,对某一范围内的所有元素进行操作,而不必关心保存这些元素的容器。因此,这些通用的代码可以和任何能够生成迭代器的容器一起使用。

组合优于继承

“四人帮”的最重要的贡献也许并不在于提出了模式的概念,而在于在该书的第1章中介绍的那句格言:“对象组合优于类继承。”理解继承和多态性如此具有挑战性,以至于人们可能对这些技术赋予了不适当的重要性。我们看到许多由于“继承嗜好”而导致的过于复杂的设计(包括我们自己的设计)—比如,由于坚持到处使用继承致使许多多重继承设计得到发展。

《极限编程》(Extreme programming)的指导原则之一是“只要能用,就做最简单的”。一个似乎需要继承的设计常常能够戏剧性地使用组合来代替而大大简化,从而使其更加灵活,在学习过本章中的一些设计模式之后读者将会理解这一点。因此,在考虑一个设计时,问问自己:“使用组合是不是更简单?这里真的需要继承吗?它能带来什么好处?”

[1]为方便起见,书中的例子都是使用C++描述的;遗憾的是这种标准出现在C++前的方言缺乏一些诸如STL容器等现代语言特征。

[2]许多材料来源于“Thinking in Patterns:Problem-Solving T echniques using Java”,可从网站www.MindView.net上得到。