阅读者(十八):编码的艺术

这是一本关注编码细节的书。或许你会认为本书所讲皆为小道,诸如方法命名、变量定义、语句组织、任务分解等内容,俱是细枝末节,微不足道。然而,对 于一个整体的软件系统而言,既需要宏观的架构决策、设计与指导原则,也必须重视微观的代码细节。正如作文,提纲主旨是文章的根与枝,但一词一句,也需精雕 细作,才能立起文章的精气神。所谓“细节决定成败”,软件历史上,有许多影响深远的重大失败,其根由往往是编码细节出现了疏漏。

我一直坚持“代码即架构”的观点,正如小说需要角色来说话一般,软件系统的质量好坏,归根结底还是需要代码来告知。代码的优劣不仅直接决定了软件的质量,还将直接影响软件成本。Yourdon【1】和Constantine【2】在著作Structured Design中 认为:软件成本由开发成本与维护成本组成,而往往维护成本要远高于开发成本。这其中付出的主要成本就是由于理解代码和修改代码造成的。正如本书的书名 The Art of Readable Code所表示的含义,好的代码常常是可阅读的,要做到这一点,则近似于一种艺术之美了。

与本书相似的一本书是Robert C. MartinClean Code(中文版:代码整洁之道)。该书在业界已经得到了广泛赞誉。如果你还在为写出丑陋的代码而烦恼,必须阅读该书。它带给你的冲击,好似阿凡达那无与伦比的3D效果带给你感官上的震撼。在某种程度上,The Art of Readable Code一 书几乎可以与Clean Code比肩。或许本书在深度上与Clean Code相比还有所不及,但在内容广度上,却超过了Clean Code。因为它关注编码本身,所以并不局限于某一种语言,而是列举了大量C++、Python、JavaScript和Java代码,涵盖了主流的静态 语言和动态语言。这就使得本书的内容具有更强的普适性。

本书一共分为四部分。第一部分Surface-Level Improvements主要介绍了变量、方法和类的命名,以及如何编写好的注释。第二部分Simplifying Loops And Logic则将注意力转移到语句和表达式。第三部分Reorganizing Your Code则主要介绍了有关任务分解、职责分配和设计意图的内容。最后一部分是一些扩展话题,例如如何编写测试,提高代码可读性,并在最后一章给出了一个完 整的案例,全面而又系统地结合案例回顾了全书所讲的主要内容。

本书给出了许多改善编码质量的技巧,尤其它结合了大量真实案例,给出了具体的代码片段,并从正反两面对案例进行分析,这就使得作者的讲解不再流于空洞,既让人信服,又能帮助读者理解。

例如在讲解命名如何表达意图时,作者给出了Google代码中的一个反面教材(之所以作者能够给出Google代码的案例,是因为本书的两位作者Dustin BoswellTrevor Foucher曾经或者正就职于Google )。例如在Google的一段代码中定义了一个宏,用于禁止“邪恶”的构造函数:

classClassName {
    private:
        DISALLOW_EVIL_CONSTRUCTORS(ClassName);
    public:
    …
};

宏的定义如下:

#define DISALLOW_EVIL_CONSTRUCTORS(ClassName) \ 
ClassName(constClassName&); \ 
void operator=(constClassName&);

这个宏禁止了构造函数和Copy构造函数(即=操作)。然而从宏的名称来看,这个含义是不明确的,会让读者认为仅仅禁用了构造函数。只需要改个名字,意图就可以变得更加清晰:

#defineDISALLOW_COPY_AND_ASSIGN(ClassName) […]

书中各章的案例非常翔实,并且总是先给出糟糕的版本,逐步分析推导,最后给出好的实现,作为直观鲜明的对照。为避免读者陷入相对独立而散乱的小案例 中,作者又专门独立出一章内容,讲解了一个完整的案例Minute/Hour Counter。该案例从问题域的提出,到对接口的设计和实现,层层推进。作者从方法命名和注释开始,抽丝剥茧展开分析,先后给出了三个解决方案,渐进地 对编码实现进行了改善,使之在性能、灵活性都有了很好的改观,类的职责更为清晰,代码结构简洁而又易于理解。最后,作者还给出了三个解决方案的比较,从代 码行数、时间复杂度、内存消耗和准确率四个因素出发,全面权衡了各个解决方案的优劣,以此来印证作者在本书中一直推崇的编码技巧。

本书作者并不满足于通过文字和代码来彰显这些技巧的力量,书中附带的漫画插图起到了很好的辅助作用。如果将本书比作一盘精美的佳肴,这些漫画就起到了调料的作用。例如作者希望表达出名字误用所带来的危险时,给出了这样的一幅漫画:

图文并茂、案例翔实是本书的主要特色。在阅读本书时,代码从丑陋到美丽的蜕变让人振奋;但是,我们不能满足于结果的获得,而应该享受这个过程。我的 建议是,在阅读时,多思考作者给出的反面教材,不要急于了解结果,而应掩卷遐思,分析这段代码的问题,并结合自身经验与能力给出自己的方案。然后再比较作 者的方案,两相印证,辨别两个方案各自的优劣之处。最后再去仔细阅读作者的分析过程,才能更好地理解本书,提升自己的编码能力。

唯一让我觉得美中不足的是书中第14章。这一章主要通过一个实际案例,讲解了测试与代码可读性之间的关系。这个案例是一个测试方法,作者认为代码至 少存在8个问题,并由此展开对这一测试方法的分析。然而,或许作者过于追求代码的优雅,使得本来较为简单的测试变得相对复杂,引入了许多不必要的代码。尤 其是引入的ScoredDocsFromString()方法,它是为测试服务的辅助方法,如下所示:

vector<ScoredDocument>ScoredDocsFromString(string scores) {
    vector<ScoredDocument> docs; 
    replace(scores.begin(), scores.end(), ',', ' ');
    // Populate 'docs' from a string of space-separated scores. 
    istringstream stream(scores); 
    double score; 
    while (stream >> score) {
        AddScoredDoc(docs, score); 
    }
    return docs;
}

对于一个测试辅助方法而言,它的逻辑显得过于复杂了。最关键的是对于这样一个相对复杂的方法,而它本身又属于测试的一部分,该谁来保证它自身的正确性呢?这样对测试代码的改写有过度设计的嫌疑,事实上加大了理解测试的复杂度。作者在书中也写到:

This may seem like a lot of code at first glance, but what it lets you do is incredibly powerful.Because you can write an entire test with just one call to CheckScoresBeforeAfter(), you’ll be inclined to add more tests.(as we’ll be doing later in the chapter).(粗略一瞥,好似代码量增加了,实则它会给你带来难以置信的威力。这是因为现在所写的整个测试仅仅调用了一次 CheckScoresBeforeAfter()方法,但在将来你会添加更多的测试。我们将在本章后面讨论)。

遗憾的是,直至本章末尾,我也未曾看到作者有足够说服力的分析,让我愿意为了这样的重用性与灵活性做出如此复杂的实现。不过,本章末尾指出测试方法存在的8个问题,确实精辟独到,值得我们反复阅读,细细体味个中奥秘。

全书一共204页,与那些瀚如烟海的高文大册相比,本书显得轻而薄,但它胜在精专。作者没有囊括所有编码技巧的野心,更没有卖弄地展现自己的设计技 巧和博识广学。它的专注可能会因此失去一大部分读者群,但这样的书才是我们程序员真正需要的。遗憾的是目前国内并没有出版本书的中文版或影印版。幸运的 是,在本书出版商O’Reilly的官方网站上,给出了本书的免费在线版本。有兴趣的读者可以通过访问http://ofps.oreilly.com/titles/9780596802295/阅读本书。

【1】Edward Yourdon:世界知名计算机专家,面向对象设计大师,Coad/Yourdon面向对象方法学的开发者之一。他出版了数十本著作,包括Death March(即中文版《死亡之旅》)、The Decline and Fall of the American Programmer与The Rise and Resurrection of the American Programmer等,并发表了550多篇技术论文和文章。Yourdon是ACM、IEEE、PMI和ITLA的成员。

【2】Larry L.Constantine是澳大利亚悉尼理工大学计算机科学教授,专门讲授软件工程和组织变更管理。他是最早从事结构化设计以及现代软件工程理论与实践的许多核心概念和模型研究的专家,是一位国际公认的软件人类因素方面的权威。他一共发表了150多篇论文和文章,出版了10余部专著,其中包括《Software for Use》(该书获得了1999年的Jolt Product Excellent大奖)。

This entry was posted in Best Practices. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s