说明:本读书笔记是本人在学习Design Patterns Explained(英文版)时的备忘录,绝大部分内容是该书的简化翻译,同时在学习过程中参考了GoF的经典著作以及天火博客上面的相关内容,为此向他们表示感谢。尤其对于天火,虽然我并不知道他是何方神圣,但是对于他在笔记中所体现出的深刻理解以及对翻译的准确把握能力让我十分佩服,非常感谢他的博客对我在学习过程中发挥的巨大作用。由于本人一向懒惰,且另有杂务,因此本读书笔记将断续出现,甚至中途夭折,希望学习设计模式的广大朋友多提意见!
1 The Object-Oriented Paradigm
1.1 Before the Object-Oriented Paradigm: Functional Decomposition
任务描述:从数据库中读取图形信息并加以显示。
解决方案1(自然的步骤):
1、在数据库中定位图形列表;
2、打开图形列表;
3、根据一定的规则对其排序;
4、显示单个图形。
对以上各步骤中又可以根据功能进行分解,如上述第四步分解为:
4a 、识别图形类别;
4b、得到图形存储的位置;
4c 、调用合适的函数显示图形。
这种分解的方式称之为功能分解,他将问题分解为多个较小的功能模块,这样更容易实现。功能分解存在的问题:1、它需要一个主程序负责控制其他各个子程序,赋予了主程序太多的职责,这样代码变得较为复杂;2、它难以适应未来需求的变化,在需求变化时,维护代价高昂而且易于引入错误。
1.2 The Problem of Requirement
对于需求而言,它永远都是不完整、局部性、经常错误而且具有错误导向性,我们不可能期望一次就将所有需求全部弄明白,当然也不能放弃对需求进行完善。我们不能左右需求的变化,但是我们应尽可能地预测需求可能发生变化的地方。对于需求变化,我们适应它并且处理它,这是较好的方式。
1.3 Dealing with Changes: Using Functional Decomposition
采用这种方式就是将较大的问题分解为多个小问题进行处理,如对于上述图形显示任务中的步骤 4c ,采用功能分解后进行模块化的实现方式可能如下:
Function: display individual shape
Input: type of shape, description of shape
Action: switch (type of shape)
Case square: deal with square display
Case circle: deal with circle display
采用这种模块化的好处是代码易于理解,也易于维护,但它不能处理可能发生的变化,比如增加新的图形类型。采用功能分解存在的主要问题在于需求变化导致开发与维护量很大,而且对于某些函数集的改变可能导致其他相关函数或数据出现问题,这样陷入修改错误的泥潭中不能自拔。
附:(名词解释)
Cohesion: how closely the operation in a routine are related,即一个程序内部操作之间的联系程度;
Coupling: the strength of a connection between two routines,即两个程序之间的联系的强度。
对于程序设计而言,目标在于创建具有内部完整性(即强cohesion)的程序,以及小的、直接的、可见的、灵活的与其他子程序之间的联系,即程序之间具有松耦合的关系。
Unwanted side effect: 即对某个函数或数据的修改导致对其他代码造成不可预期的影响,这样所造成的Bug。
1.4 Dealing with Changing Requirements
本节引入一个新的任务,其描述如下:假定你是一个教师,在你班上上课的同学下课后需要到另外一个教室去上另外一门课程,而你的一个职责就是让每一个同学都知道怎么去另一个教室上课。
解决方案1:
1、得到班上同学名单;
2、对每一个同学作如下处理:
a、查找他将去的下一个教室名称;
b、查找下一个教室所在的地点;
c、查找从本教室到下一个教室的路线;
d、告诉该同学如何到达其将要去的教室。
为达到以上目的,可能存在以下的过程:
1、得到班上同学名单的方法;
2、得到各个同学的日程表(此处专指下一个课程安排)的方法;
3、一个程序提供从本教室到其他教室的指示;
4、一个控制程序处理各个同学(到另外一个教室)所需要的步骤。
解决方案2:
教师并不对每个同学单独作全部事情,他只是告诉同学如何从本教室到其他教室的路线,各个同学根据各教室间的联系信息以及自己的安排找到自己所要到达的教室,而这种方案的前提是:各个同学都知道他的下一个课程,下一个课程的上课地点以及根据指示他们可以到达正确的教室。
上述两种方案最大的不同在于职责转变方面,对第一种情况而言,他需要教师显式为每个同学指明路线,没有其他任何人帮助作任何事情,教师需要为一切负责,而第二种情况仅给出一个通用的指示,期望各个同学自己能独自处理相关的事情,从而到达正确的目标。
现在来考虑在上述两种方案中当需求发生变化时将引起的问题:现在引入研究生作为课程的助理,而研究生在下课后需要先将作业等资料送回办公室,然后才到他该去的教室上课。
在方案1中,教师需要修改控制程序来区分研究生与普通同学,然后对研究生特别的指令,这样就需要修改程序的多个部分。
而在方案2中,教师只需要为研究生写一个附加的程序,而对控制程序不作改变,只需要发出“到下一个教室”的指令,而各个同学根据自己不同的类型选择不同的处理方式。
对比两种不同的方案,主要的不同如下:
1、谁负责的问题:在方案1中,教师负全部责任,而方案2中,同学自己负责;
2、控制程序的类型识别:方案1中,需要针对不同类型的学生作不同的处理,而方案2中他可以将不同类型的学生看成相同的类型加以处理;
3、控制程序的任务:方案1中,控制程序必须对每一个同学的每一个步骤都要加以处理,而方案2中则完全忽略,各个步骤由同学自己处理。
附:UML中的名词解释
Conceptual: related with responsibility—what is responsible for?
Specification: related with software interface – how to use?
Implementation: related with code itself—how fulfill responsibility?
1.5 The Object-Oriented Paradigm
一般而言,对象是数据与方法的集合,但这种理解具有很大的局限性,更好的理解是:对象根据其职责来考虑,即好的设计规则应该为对象为他们自己负责并且其职责应该明确加以定义。根据Flower的观点从三个层次来看:
从概念层次上看,对象是职责的集合;
从规格层次上看,对象是方法(行为)的集合,这些方法可被其他对象或自身激活;
从实现层次上看,对象是代码与数据的交互。
不幸的是,大多数人只是从实现层次上来看对象,来学习以及谈论OO设计,而忽略了更为重要的层次――概念与规格层次。
1.6 Object-Oriented Programming in Action
本节考虑本章开始提出的“图形显示”任务,采用OO的方式来实现。
根据对象与其相应的职责划分后的主程序如下:
1、主程序创建图形库对象;
2、主程序要求图形库对象查找其中的图形类型集合;
3、主程序要求图形类型集排序其中的图形;
4、主程序要求图形类型集显示其中的图形;
5、图形类型集要求各个图形显示他们自己;
6、每个图形调用自己的显示函数显示自己。
采用封装的优点在于:
1、用户使用更为方便,不需要考虑其内部实现;
2、内部实现不需要考虑调用者而自由改变;
3、对象的内部实现相互独立;
4、降低不可预期错误的出现。
1.7 Special Object Methods
本节讲述构造函数以及析构函数的相关基础知识。