文档章节

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

naughty
 naughty
发布于 2018/09/01 13:11
字数 2089
阅读 4684
收藏 9

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

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

大家也可以去看作者在google演讲时的视频和他演讲的slides

复杂性的本质

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

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

关于复杂性的症状:

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

引起复杂性的原因:依赖和晦涩。

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

我的解读:这本书讲的是软件设计的哲学,哲学要解决的是最根本的问题。作者认为软件设计要解决的最根本的问题就是避免复杂性,依赖和晦涩是造成软件负责的主要原因。依赖很多时候是无法避免的,但是应该尽可能的减少依赖,去除不必要的依赖。软件设计应该容易理解,晦涩是引起复杂性增加的另一个原因。这个核心观点是这本书的主旨,借用老爱的话“Simple,but not simpler!”

我曾经就职某存储巨头,其中有一块代码因为是收购的产品,代码已经非常陈旧了,因为没有人能看懂,所以也就没有人敢修改。你看,这个产品不是也卖的挺好的。 

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

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

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

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

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

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

模块要有深度

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

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

信息的隐藏和泄漏

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

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

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

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

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

不同的层,不同的抽象

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

把复杂性向下移

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

合并还是分离

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

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

在以下的情况下,需要考虑合并:

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

确保错误终结

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

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

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

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

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

 

© 著作权归作者所有

naughty

naughty

粉丝 412
博文 79
码字总数 156805
作品 3
其它
私信 提问
读书笔记:A Philosophy of Software Design (二)

接着上次的分享 设计两次 这里“设计两次”的意思是无论设计一个类,模块还是功能,在设计的时候仔细思考,除了当前的方案,还有那些其它的选择。在众多设计中比较,列出各自的优缺点,然后选...

naughty
2018/09/02
1K
0
阮一峰:如何降低软件的复杂性?

John Ousterhout 是斯坦福大学计算机系教授,也是 Tcl 语言的创造者。 今年四月,他出版了一本新书《软件设计的哲学》(A Philosophy of Software Design)。这是课程讲稿,160多页,亚马逊全...

程序师
2018/09/11
0
0
complete_code_Chapter1

《代码大全》读书笔记 Chapter 1 Welcome to Software Construction 第一章 欢迎进入软件构建的世界 What Is Software Construction 什么是软件构建 开发计算机软件已是一个复杂的过程。在软...

Frost729
2018/08/02
21
0
ThoughtWorks(中国) 程序员读书雷达

ThoughtWorks(中国)程序员读书雷达 软件业的特点是变化。若要提高软件开发的技能,就必须跟上技术发展的步伐。埋首醉心于项目开发与实战,固然能够锤炼自己的开发技巧,却难免受限于经验与学...

aoniao
2013/04/22
21.4K
55
《Linux内核设计与实现》读书笔记 - 目录 (完结)

《Linux内核设计与实现》读书笔记 - 目录 (完结) 读完这本书回过头才发现, 第一篇笔记居然是 2012年8月发的, 将近一年半的时间才看完这本书(汗!!!). 为了方便以后查看, 做个《Linux内核设计...

你的猫大哥
2018/01/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

用于电话号码验证的综合正则表达式

我正在尝试综合使用正则表达式来验证电话号码。 理想情况下,它将处理国际格式,但必须处理美国格式,包括以下内容: 1-234-567-8901 1-234-567-8901 x1234 1-234-567-8901 ext1234 1(234)...

javail
30分钟前
5
0
你为什么要使用表达 >而不是Func ?

我了解lambda和Func和Action代表。 但是表情让我难过。 在什么情况下,您将使用Expression<Func<T>>而不是普通的旧Func<T> ? #1楼 我想添加一些关于Func<T>和Expression<Func<T>>之间的区别......

技术盛宴
45分钟前
5
0
用最简单的方法实现原生JS放大镜特效

<html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <style> *{margin:0px;padding:0px;} #big{width:200p......

汤清丽
49分钟前
4
0
NIO 编程

1. NIO 比 传统 IO 有什么优点? 答:NIO 一个 线程 可以监听多个客户端,传统 客户端 -- 服务端模型中,一个线程监听一个客户端,导致线程消耗过多,一个线程要分配0.5M~1M内存。 2. NIO服务...

杨凯123
49分钟前
4
0
SpringBoot 系列教程自动配置选择生效

191214-SpringBoot 系列教程自动配置选择生效 写了这么久的 Spring 系列博文,发现了一个问题,之前所有的文章都是围绕的让一个东西生效;那么有没有反其道而行之的呢? 我们知道可以通过@Co...

小灰灰Blog
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部