程序开发原理
——抽象、规格与面向对象设计
Barbara Liskov 、John Guttag 著
关于翻译风格:
前言 Preface
构建产品级质量的程序——可以在很长一段时间内使用的程序——众所周知是极其困难的。本书的目标就是改善程序员解决这项任务的效率。我希望读者在阅读本书之后成为一名好程序员。我相信本书的成功在于改善编程技巧,因为我的学生告诉我这已经发生在他们身上。
怎么才算是一名好程序员?是产生整个程序产品的效率。关键是要在每一阶段减少浪费掉的努力。解决的方法包括:在开始编写代码之前就仔细考虑你的实现方案,通过未雨绸缪的方法来编写代码,使用严格的测试在早期发现错误,以及仔细注意模块化编程,这样当错误出现时,只需要改动极少数代码就可以修正整个程序。本书涉及所有这些领域的技术。
模块化编程(Modularity)是编写好程序的关键。把程序分解成许多小模块,每一个模块通过良好定义的狭窄接口和别的模块交互作用(interact)。有了模块化,可以修正一部分程序中的错误而不考虑程序的其他部分,而且可以仅仅理解一部分程序而不必理解整个程序。没有模块化,程序是一大堆有着错综复杂的相互关系的部分的拼凑。很难去领悟和修改这样一个程序,同样也很难让它正常工作。
因此本书的重点在于创建模块化的程序:怎样把程序组织成一系列精心挑选的模块。本书认为模块化就是抽象(abstraction)。每一个模块意味着一个抽象,比如说指引一系列文档中的关键字的目录,或者在文档中使用目录来查找匹配某个问题的文档的过程。着重强调面向对象编程思想——在程序中使用数据抽象和对象的思想。
怎样使用这本书? How Can the Book Be Used
本书《程序开发原理》有两种使用方法。其一是作为课本教材,讲述如何用面向对象的方法来设计和实现复杂系统;其二是编程专家使用,帮助他们改善编程技能,增进他们的关于模块化和Object-Oriented(面向对象)设计的知识。
作为教材使用时,本书一般作为第二或第三门程序设计课程。我们已经在MIT使用本书很多年,给大一大二的本科生教授第二门编程课。在这一阶段,学生们已经知道怎样编写小程序。课程在两方面利用这一点:让学生更仔细地思考小程序,以及教他们如何利用小程序作为组件构建大型程序。这本书也可以在专业(如软件工程)后期教学中使用。
这本书讲什么?What Is This Book About
程序模块Program Modules
这一部分的书集中讨论抽象机制(abstraction mechanism)。它讨论procedure(子程序)和exception(异常),数据抽象,遍历(iteration)抽象,数据抽象系列(family)以及多态(polymorphic)抽象。
在对抽象的讨论中,三个步骤是重要的。首先是决定被抽象的东西到底是什么:它提供给它的用户哪些行为。创造抽象是设计的关键,因此本书讨论如何在众多选择中挑选,以及怎样才能创造出好的抽象。
第二步是通过为一个抽象制定一个规格(specification)来获取它的意义。如果没有一些描述,一个抽象就会含糊不清,而变得没有使用价值。specification则提供了需要的描述。本书定义了一种specification的格式,讨论了一份好的specification应有的属性,并且提供了许多示例。
第三步是实现抽象。本书讨论怎样设计一份实现,以及在简洁性和执行性能之间怎样权衡利弊。书中强调封装(encapsulation)的重要性以及在一份实现中履行规格中定义的行为的重要性。书中同样提供一些技术——尤其是不变式断言(representation invariant)和抽象函数(abstraction function)——来帮助读者理解代码和它的原因。不变式断言和抽象函数都实现到尽可能的程度,这对于除错和调试很有用。
关于类型层次(type hierarchy)的材料注重讨论使用它作为抽象的技术——一种把相关联的一组数据抽象归入同一系列的技术。这里很重要的一点是,是否应当将一个类型作为另一个类型的子类。本书定义了替换原则——通过比较子类和父类的specification,来决定是否建立子类关系的方法[1]。
编写大型程序 Programming in the Large
本书的其后部分讲解怎样用模块化的方法设计和实现大型程序。它建立在前文有关abstraction和specification的材料的基础之上。
编写大型程序涵盖四个主要议题。首先讲解需求分析——怎样才能领悟程序中需要什么。本书讨论怎样实施需求分析,也讨论书写产生的需求规格的方式,通过使用一种描述程序的抽象阶段的数据模型。使用这种模型将产生一份更为正式的specification,同时它也使需求检查更加严格,这样可以更好的领悟需求。
编写大型程序的第二项议题是程序设计,这通常是一个循序渐进的过程。设计过程围绕构建有用的抽象来组织,这些抽象作为整个程序之中理想的构建组建。这些抽象在设计时被仔细的编写规格,这样当程序实现时,那些实现抽象的模块可以独立地开发。这种设计使用设计笔记编写文档,包括描述整个程序结构的模块间依赖性的图示。
第三项议题是实现和测试。本书讨论了前置设计分析对于实现的必要性,以及怎样进行设计复审。它同样讨论了设计和实现的顺序。这一部分比较了自顶而下与自底而上的组织方式,讨论如何使用驱动程序和占位程序[2](stub),并且强调了制定一个事先的顺序策略的必要性,以满足开发组织和客户的需求。
[1] 译注:如果子类的specification包括了所有父类的specification,就是说父类的要求也是子类的要求,或者子类的要求更为严格,那么可以建立父子关系。而替换原则的说法是,对于具有父子关系的类,任何需要一个父类对象的地方,都可以替换为一个子类对象。
[2] 译注:在测试某一组建时,由于其余组建还未实现,这一组建与其余组建的接口衔接部分无法工作。此时可以针对这一组建编写其余组建的占位程序(stub),预留出接口的衔接代码。占位代码通常不做任何有价值的事情,只报告组建的衔接部位工作正常。
[3] 译注:作者指的是设计模式的开山之作——《Design Patterns—Elements of Reusable Object-Oriented Software》,作者为设计模式界著名的“四人帮”GoF(Gang of Four)。此书详尽讨论了三大类共23个广泛使用的设计模式的适用范围、依存关系、实现细节以及已有的应用领域等问题。书中以C++和Smalltalk为示例语言,不过书中所涉及的模式适用于所有面向对象的语言。
Github Issue 留言
Disqus 留言