当我读了《软件设计的哲学》......

程序员书库(ID:OpenSourceTop) 编译

书单来自:https://lethain.com/notes-philosophy-software-design/

最近,我读了一本朋友推荐的《软件设计的哲学》,它相当简洁,只有160页,用了几天时间把它读完了。



这本书提到了关注系统的复杂性这一新理念,并希望读者“在软件的生命周期中通过复杂性来引导软件的设计”。对此,作者还开了一门关于软件设计的本科课程,模仿论文教学的方式(初稿,写作,评论,重写,评论,再重写),并将这种方式和多年的系统开发经验相结合,以此来开展软件的复杂度类别。


作者简介



John Ousterhout 是斯坦福大学计算机系教授,也是 Tcl 语言的创造者。John Ousterhout1975 年获得耶鲁大学物理学学士学位,1980 年获得卡内基梅隆大学的计算机科学博士学位


读书笔记


我身边的朋友都很推荐这本书,认为它是很适合在代码审查过程中使用的一种工具,它提供了一系列的危险提示,包括信息泄露、浅层模块、名称模糊、实现文件接口污染、连接方法,以及特定的混合文件等


下面,分享一些我在书中看到的一些有趣的部分:


复杂性是使软件难以理解或难以修改的重要因素。


虽然作者对于复杂性的定义很宽泛,但随着本书内容的深入,这个概念会越来越清晰


如果能把复杂性隔离在一个模块,不与其他模块相关联,就达到了消除复杂性的目的。


我想这说的已经很明显了,这一点也给我留下了深刻的印象。我们没有充分思考到底是哪里产生了复杂性,如果我们做的好的话,就可以快速地让我们的系统变得更简单。


相比作者而言,阅读代码的人更容易察觉到代码的复杂性

如果其他人都认为一段代码很复杂,那么它就是复杂的。


这种说法是老调重弹,但却很有道理。但令人惊讶的是,人们对这种观点大多很抵触,包括我自己。


书中列举了三种复杂(complexity)的类型:变更扩大化(change amplification),认知负荷(cognitive load),无法预知的一切可能(unknown unknowns)。当局部修改会引起其他多个地方改变的时候,变更扩大化是最优的解决方案


减少每个设计模块所影响的代码量,变更的时候修改的代码就


认知负荷要求我们注意力从计算代码的行数上转移到接受更多更简单的代码行;这样的代码比少而复杂的代码要简单的多。(当我开始写Go代码的时候,一直在纠结这个问题。每行代码都尽可能简单,但它却比我之前用Python编写的时间还长。


最后,无法预知的一切,是你想要了解的东西,但是没有找到办法可以从代码本身学习到的。


然后,开始转到了复杂性的定义:


复杂性是由模糊性(obscurity)和依赖性(dependencies)造成的。


还有复杂性的组成因素的定义是:


依赖性是指模块无法独立于其他模块而被理解。

模糊性是指在重要信息不明确的情况下

这往往是因为缺乏文档。


复杂性管理为什么如此具有挑战性?这是因为


复杂性是递增的,前面做出的每一个决策,都可能导致后面的设计越来越复杂。


为了避免复杂性不断递增,他建议区分战略性编程和战术性编程。


战术性编程是专注于让代码能够跑起来,但这使得它几乎不可能产生好的系统设计。


相反,战略编程会转移目标重心


战略性编程是意识到光有能运行的代码是不够的

最主要的目标应该是有一个不仅能够运行,还能解决问题的好的系统设计


有趣的是,这个建议并不是要你把重心放在的前期设计阶段,而是你应该随着时间的推移不断做出大量的设计改进。这与“敏捷设计”略有不同,因为敏捷过于关注特性,然而


开发的递增应该是抽象的,而不是仅仅是某些特性。

......

理想情况下,假如你从一开始就考虑到更改系统设计时,会带来的变化,那么当你完成每个变更时,系统就会变成你一开始预想的那个结构


很多人会反对这种对抽象的关注,认为它没有什么太大的用处,因为你并不需要它,他会认为


一个好的设计很快就能得到回报。但这些策略都不太可能实现

速度更快的战术性编程都做不到了,更不用说战略性编程了


该部分特别驳斥了快速启动和稍后修复问题的创业心态,认为这是一种错误的方式。


管理复杂性最重要的方法是从接口转移到实现中:


模块是接口和实现。

最好的模块是接口比实现更简单

更重要的是模块要有一个简单的接口,而不是一个简单的实现。


当你在设计模块时,要特别小心,因为


抽象是实体的简化视图,它忽略了一些不重要的细节。忽略重要的细节就会导致模糊,从而产生错误的抽象。


这种技术被称为信息隐藏:


接口(interface)本身应该很简洁,把复杂的功能隐藏在简洁接口的下边,这些知识不应该出现在它的界面中,这才是一种好的抽象


信息隐藏的另一面是信息泄漏:


如果多个模块耦合,那就把这些模块合并成一个。


这本书花了很长篇幅在讨论异常,以及异常如何偏离正常的代码流,从而带来更多的Bug问题。这是因为你


必须恢复和尝试恢复(很难)或尝试修复然后继续开发(也很难)。这在许多情况下会导致不一致。


解决方案是“定义不存在的错误”,即设计不会出现Bug的接口。 文中给出的是 unset 与 delete 的例子,前者将确保不存在某些东西,而不是确保以前存在的东西现在已经消失。 第二个例子是从列表中获取片段,对于不存在的范围而言,更容易的处理是返回无内容,抛出更多的异常。


最好的设计不是你的第一个设计,而是你应该


设计两次,采用完全不同的方法。


这个话题有一个有趣的地方,就是很多聪明的人,通常都被他们的早期经历所影响,认为他们的第一直觉就是正确的,也正是这个原因,所以他们很难利用这一技巧。


大多数大型软件设计问题与学校的根本不同,大型软件本身并不是为了解决问题而设计的,因此他们受益于多种不同的方法。


最后,战略性编程告诉我们:


如果你没有想着把设计做得更好,那你可能会把它弄得更糟。


总而言之,这是一篇非常值得读的书,强烈推荐大家去读一读,目前还没有中文版


读者书评:


@larrylv:同组的斯坦福小伙大学时上过作者的课,强烈推荐了本书。书虽薄了些,但收获不小。

@ye11ow :引用最后一章第一句话:This book is about one thing: complexity。虽然标题里带有"philosophy",但其实书里介绍的大部分内容还是挺直觉。关于异常和注释的两章反而让我收获最多

@豆花鱼:喜欢这本书道理写在前然后举例说明的讲解方式,很好读,有一些帮助。

@秋明:降低复杂度,less is more

推荐↓↓↓
程序猿
上一篇:写给刚入行的程序员 下一篇:美团DB数据同步到数据仓库的架构与实践