「如何实现流动式软件发布」线上课堂开课啦,快来报名参与课堂抽奖吧~ 了解详情
写点什么

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

2012 年 3 月 07 日

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

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

与本书相似的一本书是 Robert C. Martin Clean 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 Boswell Trevor 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 大奖)。


给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2012 年 3 月 07 日 00:008609
用户头像

发布了 109 篇内容, 共 36.4 次阅读, 收获喜欢 10 次。

关注

评论

发布
暂无评论
发现更多内容

week5

Geek_2e7dd7

架构师训练营 - 第 5 周学习总结

红了哟

架构师训练营 - 第五周命题作业

牛牛

极客大学架构师训练营 命题作业 一致性Hash算法

程序员是这样解读《隐秘的角落》

陈东泽 EuryChen

学习 程序员 隐秘的角落

最右JS2Flutter框架——开篇(一)

刘剑

flutter 前端 探索与实践

阿里内推面试,挂在了一道简单的问题上…

小新

Java 阿里巴巴 程序员 架构 面试

搞懂Spring事务失效的8大原因,轻轻松松面试过关

码哥小胖

Java spring Spring Boot

week5 学习总结

Geek_2e7dd7

记录一次拼多多Web前端面试【一面+二面+hr面】

阿文

Spring Cloud Spring Boot Web Java 面试

【week05作业】

chengjing

正确的做事比做正确的事更重要

魔曦

架构师 极客大学架构师训练营

最强总结——分布式事务处理方式

小闫

分布式 分布式锁 Java 面试 分布式存储 分布式缓存

【week05】总结

chengjing

打造Redis分布式环境下的银弹?我觉得Redisson比Redlock更胜一筹

码农月半

Java redis redis高可用 Redis项目

第五周总结

武鹏

让你大显身手——掌握RocketMQ与Kafka中如何实现事务

小谈

kafka RocketMQ Java 面试 JVM原理 大厂面试

深入理解队列:LinkedBlockingQueue源码深度解析

itlemon

阻塞队列 LinkedBlockingQueue Queue

一篇文章深入理解分布式锁

itlemon

redis 分布式锁

啃碎并发(一):Java线程总述与概念

猿灯塔

产业区块链发展迎来爆发期

CECBC区块链专委会

产业区块链 系统稳定性 应用安全性 信任的机器

公司制的黄昏:区块链重构商业世界

CECBC区块链专委会

区块链思维 裂变 契约 激励

Week5 学习总结

wyzwlj

极客大学架构师训练营

20道Redis面试题(含答案)面试官会问的我都找到了

你是人间四月天

redis Spring Cloud Java 面试 redis6.0.0 Redis项目

为你的 SpringBoot 服务生成或推送各平台的部署包

华宇法律科技

Docker k8s springboot

架构师训练营 - 第五周 - 学习总结

韩挺

这份架构PDF如何得到百度、洋码头、饿了么CTO等大咖联袂推荐?

小新

Java 架构 面试 队列

架构师训练营 - 第 5 周命题作业

红了哟

码农必备SQL高性能优化指南!35+条优化建议立马get

码哥小胖

MySQL SQL语法 sql查询 sql

Spring Boot 多数据源 Redis 配置

南南

redis Spring Boot Java 面试 Redis作者

Uniapp使用GoEasy实现websocket实时通讯

GoEasy消息推送

uni-app websocket 即时通讯

老龄化时代的人机共生:京东数科以AI机器人推动产业增长

脑极体

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