鉴于最近的趋势,在Cindy的建议下我从 John Outerhout那里拿到了《软件设计的哲学》这本书的一个副本。这本书很简短,只有160页,我在过去的几天里已经通读了一次,这里会写下关于这本书的一些笔记。
Michael Krause也非常友好地提及了John Outerhout的一个涵盖了相同内容的演讲。
作者提出了一种检查了软件复杂度,并且期望你可以在软件的整个生命周期中通过使用复杂度来引导软件设计的哲学。此外,作者开设了一门关于软件设计的大学课程,模仿教授写作论文的方法(草稿,写作,评价,重写,评价,再次重写),同时将这种经验与开发了许多系统的漫长的职业生涯相结合,以此来发展复杂度的种类和缓解措施。
作者特别推荐把这本书作为在代码审查期间的一个有用的工具,书中提供了一个关于信息泄露,浅层模块,模糊名称,实现文档被污染的接口,联合方法和模式混合物的红色标记列表。(完整的红色标记列表在文末)
如下是我发现的非常有趣的片段:
复杂度是一切使得软件难以理解和修改的存在。
在最开始,作者便给出了关于复杂度的一个宽泛的定义。而随着内容的深入,这个定义变得更加集中。
在几乎不参与交互的地方把复杂度隔离开来的效果大致等同于把复杂度消除掉。
我认为这个是非常明了的,让我感到非常触动的一个观点。我们没有充分思考我们在哪里产生了复杂度,如果我们做的好的话,我们可以很快地让我们的系统变得更加简单。
相比作者而言,读者更容易察觉到复杂度。 如果其他人说一段代码是复杂的,那么它就是复杂的。
这是一个古老的,同时也很有道理的说法。令人惊讶的是,包括我在内的人们对这种反馈是非常抵触的。
书中列举了三种复杂(complexity)的情况:变更扩大化(change amplification),认知负荷(cognitive load),以及无法了解未知流程(unknown unknowns)。变更扩大化是指当局部修改时,还需要在其他地方进行多处修改,它的最好预防方法是
减少受设计决策影响的代码数量,那么设计变更则不会需要太多的代码的修改。
认知负荷将我们的思维进行转变,从计算代码行数,变为接受更多,更简单的代码,这种代码比那些更少更复杂的代码要简单。(这是当我开始写更多的Go代码时,我所努力奋斗的目标。什么东西都很简单,只是比我之前用Python写的要长的多。)
最终,无法了解未知流程即是你想要了解一些东西,但是从代码本身中没有合适途径来了解。
然后,它转到了复杂的定义:
复杂是由模糊(obscurity)和依赖(dependencies)引起的。
还有复杂的构成因素的定义:
依赖是不能读懂独立的代码。
模糊是重要的信息不明确的情况。
这些问题通常是由于缺乏文档。
为什么复杂性管理如此具有挑战性?这是因为
复杂是递增的,这是数以千计的决策导致的结果。
这使得它很难预防,甚至更难以修复。
为了对抗复杂的蔓延,他建议区分战略编程和战术编程。
战术思维主要是让代码能够运行,但是这样几乎不可能产生出好的系统设计。
相反,策略编程改变了目标。
策略编程意识到只是运行代码是不够的。
其首要目标是一个不仅能够运行,还能解决问题的优秀的设计。
值得注意的是,这个提议不是说你应该将重点放到前期设计阶段,而是说你应该随时间的推移做大量的递增设计改进工作。这是与“敏捷设计”略有不同,因为敏捷太过专注于特性,然而
设计的递增应该是抽象的,而不是某些特性。
…
理想情况下,当你完成了每个修改,如果你一开始就考虑到该更改而设计了系统结构,系统就会具有它从一开始就具有的结构。
很多人会反对这种对抽象的关注,他们认为从你不需要它这个角度来看,抽象明显没有作用,但是他会说
优秀的设计会有快速的回报。即使在第一个版本中,战术方法都不太可能有更快的回报,更不用说第二版了。
这一章节专门驳斥了创业心态,即快速启动并在之后修复错误的二分法。
管理复杂性的最重要方法是将其从接口转移到实现中:
模块是接口和实现。 最好的模块,其界面要比实现简单的多。 ... 一个模块有一个简单的界面,比有一个简单的实现这是更为重要。
在思考设计模块时必须要小心,因为
抽象是省略的实体的简化视图而不是不重要的细节。 忽略重要的细节导致模糊,而造成错误的抽象。
这种技术被称为信息隐藏:
每个模块都应该包含一些知识,代表设计决策。 这种知识不应出现在其界面中,因此仅限于其实施。 更简单的界面与更好的信息隐藏相互关联。
信息隐藏的另一面是信息泄露:
当跨越多个模块使用设计决策时,将它们耦合在一起
这本书花了一些时间讨论异常,以及异常如何偏离正常的代码流,从而导致比代码行可能建议的问题更多的问题。这是因为你
必须恢复或试图恢复(难)或试图修复和前进(也很难)。这导致了许多情况下的不一致性。
解决方案是“定义存在的错误”,即设计接口以使这些被定义了的错误不再成为错误。 文中给出的是 unset 与 delete 的例子,前者将确保不存在某些东西,而不是确保以前存在的东西现在已经消失。 第二个例子是从列表中获取切片,对于不存在的范围而言,更容易的处理是返回无内容,而不是抛出超出界限的错误。
最好的设计不是你第一次的设计,而是应该
采用完全不同的方法,设计两遍。
关于这个话题有一个有趣的地方,提到那些非常聪明的人,往往他们在早年都有过训练,这使得他们的第一直觉总是正确的,也正是这个原因,往往导致这些人能有很好的职位级别,与此同时,这也会让他们纠结于某项技术的优点。
其它翻译版本 (1) 加载中和在学校工作比较,大多数大型软件设计问题从根本上是不同的。大型软件本身并不是为了解决问题而设计的,因此他们受益于多种不同的方法。
最后,对战略思维的结束祝福:
如果你没有想着设计的更好,你可能正在使情况变得更糟。
总而言之,这是一篇非常值得阅读的好文章,我强烈推荐大家读一读!
重点内容
下面介绍本书中的重点内容列表,但您必须购买该书才能体会真正意义!
浅模块
信息泄露
时间分解
感光过度
传递方法
重复相同的代码片段
一般特定的混用
连接方法
不明显的代码
很难描述
注释重复代码
模糊的名字
难以挑选名字
实施文档会暴露接口
2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务