什么是设计模式

美国著名物理学家R. P. Feynman指出:“要给一个概念下一个非常严格的定义往往是不值得的”。什么是设计模式(design pattern)?从1995年[GoF]出版至今,设计模式(design pattern)仍然没有被一致认可的定义。

要点:

  • 设计模式是可复用的经验的归纳或总结吗?(反思)
  • 意图和动机难以作为一个模式的学习人口。当从一个点获得一个模式后,程序员可以总结和归纳其特点,并从中提炼出该模式的最本质的特性作为其概念的描述。然而,提炼出的特性通常会见仁见智。(学习方法)
  • 演绎法。我们根据已知的原则、定理,按逻辑进行推演各种设计模式。(手段)
  • 自己获得的模式,不存在知易行难问题。(好处)


[GoF·第6章结论]中,介绍了设计模式的诞生与发展的历史:『建筑师Christopher Alexander第一个研究了建筑物和社区的模式,并开发了一个“模式语言”来生成它们。他的工作一次次地启发了我们』。对于不熟悉建筑行业、也不熟悉“模式语言”的大多数程序员,模式/pattern,显得比较高大上。不过现今模式用得越来越多,例如网络上粉丝大战进入互喷模式、某某明星开启自黑模式……模式的口语化的等价词汇,是“套路”、“技法”、“常见场景”或“方案”。

中国象棋残局的解决方案或杀法,正是一系列的模式。一个典型的残局,其盘局如图0-1所示。现在的问题:在当前的局面下,红方如何战胜黑方。


会下中国象棋的人肯定能够找出办法:炮放在将的后面打黑车,就能够锁定胜局。对于这种常用的残局杀法,人们称其为“海底捞月”。


†模式(pattern)是(软件设计时)可复用的设计思路、代码结构(UML图形)、对特定环境/条件下设计意图的解决方案。

  • [GoF·1.1什么是设计模式] 称:设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述。一个模式包含4个基本要素:名字、问题描述、解决方案和效果。
  • 一个常见的定义。设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

1.名称和新的模式

大多数人都会承认,一个模式应该有名字。模式的名字对于棋手或程序员讨论问题很有帮助,这些名称是专业素养和专业词汇的一部分。可以用“海底捞月”一个词来描述某一个特定的问题(如图0-1所示的盘局)、这一盘局下的解决方案、各种变化和解决方案的效果。海底捞月的别名是“车炮胜单车”。

文档是软件开发的重要组成部分,例如,一个框架如Spring,其文档是程序员学会使用该框架的重要资料。在文档中使用模式名,可以使懂得设计模式的人更快地了解系统。

[GoF]总结了23种典型的模式,但是新的模式是否有一个名字,由谁命名?例如依赖注入模式,以前被称为控制反转,直到Martin Fowler[1]使用依赖注入(Dependency Injection)为其命名后,被大多数人认可和接受。

中国象棋残局中,有大量模式采用直接而自然的名字,如“单马钉孤将”。软件设计中必然有许多可复用的设计思路、代码结构(UML图形),它们没有名字。


[1]Martin Fowler. Inversion of Control Containers and the DependencyInjection pattern

这就引出一个更重要的问题:设计模式是可复用的经验的归纳或总结吗?

[GoF]在人们没有认识到设计模式的时候,将典型的模式加以归纳和总结。他们不可能穷尽所有模式,一个新的设计结构如依赖注入,只要出现和存在,就诞生了一个新的模式。它并不会因为不被多数人知晓而不存在、不会因为传播问题(被反复使用)而不存在,不会因为未获得权威认定(经过分类编目的)而不存在。这里的要点和结论是设计模式归纳论调不成立

对于没有经验的程序员,经过归纳的设计模式集,是学习和复用他人经验的一个途径。『(1.8节)一旦你选择了一个设计模式,你怎么使用它呢?』,这是学习归纳的设计模式集会出现的问题,也是一个较奇怪的问题。因为从来没有一个下棋的人说“我这盘棋要使用海底捞月”,只有在出现了该残局时,才需要使用海底捞月技法——不论你是否知道该技法叫"海底捞月"。

2.推演

归纳法的对立面是演绎法。我们根据已知的原则、定理,按逻辑进行推演。

从特定的问题入手,有时候可以非常容易地“推演”出某个[GoF]已经介绍的模式。(这不是重新发明轮子!不能因为Java已经有了集合框架,所以就不需要学习数据结构。)

通过推演,程序员获得“自己的”模式,并且在推演中会考虑到该模式的各种变化、各种适合和不适合该模式使用的外部因素。更重要的是体验推演的过程!

采用面向对象技术进行设计时,会遇到各种各样的问题,例如new表达式是硬代码、if-else语句在增添分支时违反开放封闭原则、系统需要设计一个文本解释器等等。

设计时遇到的问题千变万化,推演设计模式我们从两方面着手:

①设计原则。当程序员按照面向对象范式和设计原则重新审视已有的源代码时,会发现源代码中存在诸多不符合设计原则的地方。如果能够按照面向对象范式和设计原则重构已有代码或编写新程序,将获得一个好的、以后可反复使用的设计方案——某种设计模式。

适应需求变化,是开放封闭原则追求的目标。从源代码入手,首先假定源代码的某一部分将会发生某些变化,从中程序员可以找出一些应对的手段或设计模式。[GoF·1.6.7设计应支持变化]中提供了一个索引。

 

②目的性需求。目的性需求是一个可大可小的描述。满足设计原则,也可以说是目的性的。这里所指的目的性需求,通常比较难以从原则、从源代码上,直接获得灵感的设计需求。如要解决集合的遍历、要实现undo等这样的特定目标而出现模式;以及某种特定的专门性要求,如设计解释器(学习编译原理的时候,这是程序员应该实现的)。

一个自然的疑问:从一个(目的性需求)点入手而获得的模式,能否运用于更多地方?

在[GoF·1.3描述设计模式]中,对设计模式“意图”和“动机”加以描述。事实上,从不同的角度描述同一个设计模式,“意图”和“动机”的描述就不会相同。笔者认为:意图和动机难以作为一个模式的学习人口。当从一个点获得一个模式后,程序员可以总结和归纳其特点,并从中提炼出该模式的最本质的特性作为其概念的描述。然而,提炼出的特性通常会见仁见智。

3.解决方案

设计模式是软件设计的解题模板。在推演解决方案的过程中,通常从源代码入手,(必要时)讨论结构、参与者、协作等内容,最后总结该模式的特点和思路。类图或者说代码结构是一个模式的基本表现方式。

我们希望,将解决方案/设计模式视为自己的、经过从问题分析、思考、代码验证后自己“获得”的成果,并将它们很好地记录下来,供以后使用。而不是将它们简单地视为“前辈们”的实践经验的总结。这一点非常重要。在既有的设计模式面前,我们的推演会类似几何的证明题——知道结果的推演只能够称为证明。

[最后编辑2016.10.18]


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值