读书笔记:A Philosophy of Software Design (一)

今天一位同事在斯坦福的博士生导师John Ousterhout (注,Tcl语言的设计者)来公司作了他的新书《A Philosophy of Software Design》的演讲,介绍了他对于软件设计的思考。这里我把本书的读书笔记和心得分享给你们,欢迎你们来和我交流探讨。html

你们也能够去看做者在google演讲时的视频和他演讲的slides程序员

复杂性的本质

软件设计应该简单,避免复杂,关于复杂性的定义,做者认为主要有两个量度数据库

  1. 系统是否是难以理解
  2. 系统是否是难以修改

关于复杂性的症状:编程

  1. 当新增特性时,须要修改大量的代码
  2. 当须要完成一个功能时,开发人员须要了解许多知识
  3. 当新增/修改功能时,不能明显的知道要修改那些代码

引发复杂性的缘由:依赖和晦涩。服务器

最后,复杂性不是忽然出现的,它是随着时间和系统的演进逐渐增长的。架构

个人解读:这本书讲的是软件设计的哲学,哲学要解决的是最根本的问题。做者认为软件设计要解决的最根本的问题就是避免复杂性,依赖和晦涩是形成软件负责的主要缘由。依赖不少时候是没法避免的,可是应该尽量的减小依赖,去除没必要要的依赖。软件设计应该容易理解,晦涩是引发复杂性增长的另外一个缘由。这个核心观点是这本书的主旨,借用老爱的话“Simple,but not simpler!”less

我曾经就任某存储巨头,其中有一块代码由于是收购的产品,代码已经很是陈旧了,由于没有人能看懂,因此也就没有人敢修改。你看,这个产品不是也卖的挺好的。 ide

仅仅可工做的代码还远远不够

在第二章,做者提出了“战术性编程”“战略性编程”的对立。微服务

“战术性编程”最求以最快的速度完成可工做的功能。这看上去无可厚非。可是这种行为每每会增长系统的复杂性。引起大量的技术债。能够说这种作法以牺牲长远利益来得到眼前的利益。google

“战略性编程”不只仅要求可工做的代码,以好的设计为重,将来的功能投资,认为现阶段在设计上的投入会在未来得到回报。

好的设计是有代价的,问题是你愿意投入多少?

个人解读:颇有趣的是,我司以前的产品的负责人在公司推行大规模的敏捷(LeSS),当时有一个顾问给咱们上课,他也说设计要尽量简单,可是不要为了将来作设计。以最小的代价实现可用的功能。以John的观点,这样作无疑会增长系统变复杂的可能性。我比较认同John这里的观点,好的设计是有价值的,投入在软件设计上的,对功能毫无影响的东西,是有价值的。可是如何取舍和权衡,投入多少是须要开发团队达成共识。 软件有它的生命周期,为了将来的投入也不是越多越好。

模块要有深度

深度实际上是对模块封装的度量,模块应该提供尽量简单的接口和尽量强大的功能。这样的模块称之为深度模块。

个人解读:这一部分没有什么新东西,传统的面向对象和现在的微服务架构都是对这一哲学的应用。好的封装能够减小依赖,简单的接口能够避免晦涩。也就是减小了复杂性。

信息的隐藏和泄漏

关于信息的隐藏和泄漏,这一部分对于熟悉面向对象的猿们来讲不是新东西。基于SOLID,这就是Open,软件应该是对于扩展开放的,可是对于修改封闭的。信息隐藏使得修改变的封闭。

具备通用功能的模块更具深度

更通用功能的接口意味着更高层级的抽象,隐藏更多的实现细节,按照John的观点,也就更具深度。那么如何在通用接口和特殊接口之间作权衡呢?

  1. 可以实现所需功能的最简单接口是什么?
  2. 该接口会被用于那些不一样场景?
  3. 该接口对于个人当前是否容易使用?

个人解读:通用的接口和以前的“战略性编程”是一致的,更通用的接口在面对将来可能发生的需求变化的时候,更容易使用。这里的艺术在于可以找到需求到软件接口之间的最佳映射。抽象到哪个层级,是主要问题。

不一样的层,不一样的抽象

软件系统一般有不一样的层次组成,每一层都经过和它之上和之下的层的接口来交互。每一层都具备本身不一样的抽象。例如典型的数据库,服务器和客户端模型中,数据库层的抽象是数据表和关系,服务器层是应用对象和应用逻辑而客户端的抽象是用户接口视图和交互。若是你发现不一样的层具备相同的抽象,那也许你的分层有问题。

把复杂性向下移

在软件分层的鄙视链中,最高层是用户,接着的一层的UI工程师,而后是后台工程师,数据库工程师,等等。用户是上帝不能得罪,若是必定要在某个层次处理复杂性,那么这个层次越低越好,反正苦逼程序员也不会抱怨,对得,就是这个道理。

合并仍是分离

“天下大事,分久必合,合久必分”。软件设计中常常要问的问题就是这两个功能模块是合并好,仍是分开好?不管是合并仍是分离,目标都是下降复杂性,那么把系统分离成更多的小的单元模块,每个模块都更简单,系统的复杂性会下降么?答案是不必定:

  • 复杂性可能来源于系统模块的数量
  • 更多的模块也许意味着须要额外的代码来管理和协调
  • 更多的模块可能带来许多依赖
  • 更多的模块可能带来重复的代码,而重复的代码是恶魔

在如下的状况下,须要考虑合并:

  • 模块之间共享信息
  • 合并后的接口更简单
  • 合并后减小了重复的代码

确保错误终结

异常和错误处理是形成软件复杂的罪魁祸首之一。程序员每每错误的认为处理和上报越多的错误,就越好。这也就致使了过分防护性的编程。而不少时候,程序员捕获了异常并不知道该如何处理,干脆往上层扔,这就违背了封装原则。

“funny error message”的图片搜索结果

用户一脸懵逼,“你叫我干啥?”

下降复杂度的一个原则就是尽量减小须要处理的异常可能性。而最佳实践就是确保错误终结,例如删除一个并不存在的文件,与其上报文件不存在的异常,不如什么都不作。确保文件不存在就行了,上层逻辑不但不会被影响,还会由于不须要处理额外的异常而变得简单。

今天就先分享到这里,后面有空,我会继续分享本书的后半部分,祝你们开学愉快!

相关文章
相关标签/搜索