第2章 第1个模式——模板方法(Template Method)模式

    本章将介绍第一个模式,也是最容易理解和使用的模式之一——模板方法模式。本章将从一个简单的场景出发介绍该模式,这个场景在后面的模式中还要继续使用。

    本章包括如下内容。

    讲述DRY原则。

    模板方法模式。

    引入回调的模板方法模式。

    2.1 从回家过年说起

    春节是中国传统节日里最热闹的,对于在外漂泊多时的游子而言,最幸福的事莫过于回家过年。为了回家团圆,我们首先需要购买火车票,然后乘坐火车,最后才能和家人团聚。

    这里编写一个简单的程序来模拟这个过程,最初的设计非常简单,编写了一个HappyPeople类,它有一个celebrateSpringFestival()的方法,把买票、回家和家里庆祝的逻辑代码都写在这个方法里,代码大致如下所示。

    figure_0030_0012

    后来,我们逐渐发现,有人需要坐火车回家,有人需要坐飞机回家,而有人坐大巴回家就可以了。但是不管你乘坐哪种交通工具回家,都得先买票,然后才能和家人团聚。于是又创建了一个新类PassengerByCoach来实现坐汽车回家过年的逻辑,由于买票和在家庆祝的代码一样,我们把类HappyPeople的代码快速复制了一份,把乘坐交通工具的那部分代码替换成了坐大巴的逻辑,结果如下。

    figure_0031_0013

    斜体加粗的部分便是我们替换的那部分代码,接着,我们同样使用复制+粘贴(Copy&Paste)方式编写了类PassengerByAir来实现表示坐飞机回家的那类人的需求。

    复制+粘贴看起来非常实用,但是没过几周,我们慢慢发现情况不妙,这几个类的代码开始变得难以维护:

    如果买票逻辑有所改变,我们需要分别修改这三个类,但有的时候,马虎的工程师不会在所有类上做相应的修改;

    而且,由于这些类的功能发生了变化,相应类的测试代码也要做改变,这样修改这些类的测试代码和修改这些类一样,出现了相同的重复修改的问题;

    最后,我们开始变得越来越担心:因为随着交通工具的增多,势必需要开发更多类和测试类,这样维护就变得越来越麻烦。

    最终我们停下来开始思考:复制+粘贴真的很好用吗?该不该编写重复的代码呢?让我们首先从DRY(Don't Repeat Yourself)原则谈起吧。

    2.1.1 DRY(Don't Repeat Yourself)

    写在DRY之前

    在这个纷繁的世界上,你能否找到一样东西,它会永久不变?

    谚语:

    The Only Thing In The World That Doesn't Change Is Change Itself.

    世界上唯独不变的是变化本身。

    不管是日月星辰,还是软件开发,变化是永恒的。在软件开发过程中,一切都在变化。

    需求变化:这是软件开发中最主要的变化,我们经常听到的各种各样的抱怨,例如,之前拟定的需求突然又变化了;客户之前并不知道他们想要什么,现在还不能确定这些就是他们想要的;以前的需求根本是错误的;之前的需求并没有列出所有情形……,这都直接影响着软件开发设计。

    技术变化:新技术不停地涌现,旧的技术被逐渐淘汰。

    团队结构的变化:团队成员并不稳定,有来的,也有去的;此外团队组织结构也在发生变化,主要体现在管理者的变化。

    政治因素的变化:公司出于各种利益的考量,采取一些行为干涉软件开发,这些行为往往是不可预料和没有回旋余地的。虽然这些行为经常被开发人员嗤之以鼻,但是这些变化真真实实地影响了软件的开发。

    其他因素变化:自然灾害,交通事故等都可能影响软件开发。

    总之,软件开发的最大的特征就是变化,变化总是在发生的,我们不能因为害怕变化而逃避它,否定它。谁能从容应对软件开发中的变化,谁就能最大程度上降低软件开发中的风险,成为这一方面的佼佼者。

    开发还是维护

    Andy Hunt和Dave Thomas 在“The Pragmatic Programmer”一书中认为,软件开发人员始终处于软件维护过程中。我个人非常认同这个观点,引用此书中的原话如下[1]

    Programmers are constantly in maintenance mode. Our understanding changes day by day.New requirements arrive as we're designing or coding.Perhaps the environment changes.Whatever the reason, maintenance is not a discrete activity, but a routine part of the entire development process.

    程序员一直处于维护状态,我们的理解每天都在发生变化,当我们设计和编码时,新的需求总是接踵而至,或许是由于环境的原因吧。不管是什么原因,维护不是一个离散的行为,而是整个日常软件开发的一部分。

    没错,不管是在应用发布之前还是之后,我们要么在修改程序错误(我们称为Bug),要么在为增强软件功能(我们称为Enhancement)而修改代码,这些都属于软件维护的范畴。总而言之,软件维护始终贯穿于软件开发的始末。

    DRY(Don't Repeat Yourself)

    DRY(Don't Repeat Yourself,不要复制自己):也叫DIE(Duplication Is Evil,即复制是魔鬼),这个原则在Andy Hunt和Dave Thomas所著的“The Pragmatic Programmer”一书中阐述如下。[2]

    Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

    每份知识在一个系统中必须存在唯一的、明确的、权威的表述。

    OAOO(Once and Only Once,仅此一次):OAOO指的是要避免代码重复,代码应该简洁,如果你发现代码“臭味”时,重复的代码是其中最严重的。

    从以上定义我们不难发现,DRY原则所涉及的范围其实要比OAOO宽泛得多,DRY涉及的范围不仅包括代码,还包括其他任何信息,例如逻辑、常量、标准、功能、服务等,而OAOO指的是不能编写重复的代码。

    我们在本书主要将着力讨论如何使用设计模式避免代码的重复。

    [1]参见“The Pragmatic Programmer”一书第二章:A Pragmatic Approach。

    [2]参见“The Pragmatic Programmer”一书第二章:A Pragmatic Approach。