前 言

代码

本书的随书代码可以在以下网址下载:http://github.com/crista/exercises-in-programming-style。

目标读者

随书代码是本书的根基,这些代码面向每一个沉浸于编程艺术的人。我写这本书是为了补充和解释这些代码,因为它们的含义并非不言自明。资深的软件开发者能够重温其惯用的代码风格,也能了解其并不熟悉的别样曲风。

本书可以作为计算机科学和软件工程专业高级编程课程的教材,并附有讲义等其他教学资料。本书不适合作为编程入门课程的教材。正所谓“欲速则不达”,按部就班地学习尤为重要。通过学习,才能由“只有一种解法”向“一题多解”转变。本书的主要目标读者为大三大四的学生,或是低年级研究生。每章末尾的练习对读者的理解程度进行了测试。推荐阅读的内容更适合研究生。

本书同样适合写作者,尤其是那些有一些编程基础,或对编程技术有强烈兴趣的写作者。 虽然存在巨大差别,但写作与编程也有许多共同点。

写作动机

20世纪40年代,法国作家雷蒙•格诺的著作Exercises in Style将同样一个故事以99种不同的写作风格描绘出来。该书是写作技巧的巅峰之作,很好地诠释了“同样的故事,不同的说法”。虽然故事本身微不足道且完全一样,但该书的亮点在于形式,而非内容。它说明了讲故事的方法影响着听众对故事的理解。

格诺的故事简单说来只有三句话。

叙事者在S公车上注意到一个长脖子、戴帽子的男子,他正在与旁边的人争吵。两小时后,叙事者又在圣拉扎尔火车站看到了这名男子,和他同行的还有他的朋友。这位朋友正在建议他在大衣上多加一颗扣子。

就这样!之后格诺就用99种不同的方式把故事复述了99遍,例如用反叙法、隐喻法和非人类视角等。

多年来,我教过许多编程课程,我发现学生一般很难理解不同的编程和系统设计方式。他们最多学过两门编程语言,所以只理解这些语言所推崇的风格,而难以接受其他风格。这并非他们的过错。回顾编程语言的历史,由于缺少关于计算机编程风格的教材,开发者在积累丰富经验之前是很难意识到这一点的。即使积累了丰富的经验,编程风格对于开发者可谓“言而不畅,聆而难明”,并且伴随许多技术争论。因此,受到格诺的启发,我决定用这些年来我所接触过的不同编程风格,完成完全相同的计算任务,以此为编程风格正名。

那么,什么是风格?在格诺的乌力波圈子(Oulipo,来源于法语,意为“潜在文学工场”)中,风格只不过是“在约束条件下完成创作”的结果,通常会基于一些数学概念,如组合或漏字文。这些约束是为了创作一些从学术角度来说有趣的作品,而与故事本身无关。这种写作方式盛行一时,造就了许多文学作品。

在本书中,编程风格也正是在特定的约束下完成编程的结果。约束既可以来自于外部,也可以是自己强加的;既可以是环境的挑战,也可以是人为的限制;既可以源于过往经历和可测量的数据,也可以仅是个人的喜好。撇开来源,约束是风格之种。正是应用不同的约束,我们才写出了层出不穷、风格迥异的代码,它们做的事完全相同,但做事的方式完全不同。

优秀的程序员必须知道,编程风格与数据结构和算法是同等重要的,前者更注重人文,而后者更偏重计算。代码不仅仅是向计算机发布指令,更是向阅读代码的人传达信息。无论何种表达①,表达方式决定并影响着表达的内容如何被听众所认知。资深程序员不仅需要写出正确、健壮的代码,更需要为了不同的目的,选择合适的方式来编程。

然而,一般来说,传授算法和数据结构比起解释编程风格间的微妙差异要简单得多。算法和数据结构方面的图书通常遵循以下流程:伪代码、解释和复杂度分析。编程图书往往分为两类:一类解释编程语言,另一类展示程序设计方式或架构模式。然而,从运用编程语言自身倡导的各种理念,到组合最终构成程序的各个元素,编程其实是一个连续的整体;语言和模式互相配合,把它们一分为二是错误的。在我看来,格诺的工作将约束条件作为解释表达风格的基础,正是编程世界中将许多重要的创造性工作统一在一起的完美模型。

① 文学或编程。——译者注

应当声明的是,我并不是第一个将约束视为解释软件系统风格的统一标准的人。关于“架构风格”(architectural style)方面的研究在很久之前就开始使用这种方法。必须承认,风格源于约束(禁止某事,某物必须存在,限制某事等)这个概念乍一看并不好理解。毕竟,谁愿意在约束下编程呢?直至偶遇格诺的杰作,我才真正理顺这个概念。

与格诺的故事相似,本书涉及的计算任务微不足道:给定一个文本文件,统计文本中的词频,并按词频从高到低输出单词列表。通常,我们称之为词频统计。本书共33章,每章用一种编程风格来完成此词频统计任务,共33种风格。与格诺不同的是,我将列出每种风格的限制,并解释示例程序。我认为针对本书的目标读者,明确地点明会比读者自己理解更重要。每章会首先描述风格限制,然后给出示例程序,再详细解释代码。大部分章节会额外涉及在系统设计中如何使用该风格,以及该风格产生的前因后果。历史是重要的,任何一门学科都应不忘初心,我也希望读者会对拓展阅读有兴趣。

为什么是33种风格?因为个人能力有限。格诺的书有99种写作风格,如果我最初也以99种编程风格为目标,我想我可能永远无法完成这本书。然而,作为本书的基础,公开的GitHub代码仓库仍会不断地扩充。33种风格被分为9种类别:悠久历史、基本风格、函数组合、对象与对象交互、反射与元编程、异常处理、以数据为中心、并发和交互。整本书以这种分类方式进行组织,将相似的风格归类到一起。当然,应当也有其他的归类方式。

与格诺的作品相似,这些风格演练仅仅是“练习”。它们只是软件领域的草稿或琶音练习,并不是完整的乐章。真实的软件常常会在系统的不同部分应用多种不同的风格,并且所有的风格可以混合、配对、杂交。

最后,尽管格诺的作品是本书的灵感来源,但是软件与语言艺术并不完全相同。软件设计决策对应着效用函数;换言之,为了特定目的,一些编写方式要好于其他方式。① 在本书中,除了一些非常明确的情况,我尽可能避免给出好与坏的判断。因为好与坏不由我来评判,而是与每个项目的具体情况有关。

致谢

感谢Richard Gabriel、Andrew Black、Guy Steele、James Noble、Paul Stickler、Pual McJones、Lauri Tratt、Tijs van der Storm,以及加州大学欧文分校INF 212 / CS 235课程(2014年冬季)的学生,尤其是Matias Giorgio和David Dinh,你们为本书的初稿提出了有价值的反馈。

感谢IFIP 2.16工作组的成员。在我第一次表述本书想法时,他们的反馈对于本书成型至关重要。

感谢Peter Norvig、Kyle Kingsbury、Sara Triplett、Jorgen Edelbo、Darius Bacon、Eugenia Grabrielova、Kun Hu、Bruce Adams、Krishnan Raman、Matias Giorgio、David Foster、Chad Whitacre、Jeremy Mac Cabe和Mircea Lungu对于GitHub代码仓库的贡献。

① 也许语言艺术也是如此,但我不太了解。



























目录

  • 前 言
  • 引 言
  • 第一部分 悠久历史
  • 第1章 往日的美好
  • 第2章 Forth风格
  • 第二部分 基本风格
  • 第3章 单片风格
  • 第4章 食谱风格
  • 第5章 流水线风格
  • 第6章 高尔夫风格
  • 第三部分 函数组合
  • 第7章 无限镜像风格
  • 第8章 骨牌风格
  • 第9章 单子风格
  • 第四部分 对象与对象交互
  • 第10章 对象风格
  • 第11章 消息风格
  • 第12章 闭域风格
  • 第13章 抽象对象风格
  • 第14章 好莱坞风格
  • 第15章 公告板风格
  • 第五部分 反射与元编程
  • 第16章 内省风格
  • 第17章 反射风格
  • 第18章 切面风格
  • 第19章 插件风格
  • 第六部分 异常处理
  • 第20章 构建风格
  • 第21章 Tantrum风格
  • 第22章 消极攻击风格
  • 第23章 声明意图风格
  • 第24章 隔离风格
  • 第七部分 以数据为中心
  • 第25章 持久表风格
  • 第26章 试算表风格
  • 第27章 漂流风格
  • 第八部分 并发
  • 第28章 参与者风格 
  • 第29章 数据空间风格
  • 第30章 Map Reduce风格
  • 第31章 双重Map Reduce风格 
  • 第九部分 交互
  • 第32章 三层架构风格
  • 第33章 RESTful风格