Blog·Tanky WooABOUTTAGSRSS

昨晚打开Kindle,准备看下这本书。然后发现已经读过了,但是却一点印象都没。发现现在健忘越来越严重了,经常一个小时前的事情都记不住了。

通过笔记时间想起来原来是在年后回京的高铁上读完的。于是靠笔记再回顾了一遍。

以后对读过的书,应当及时整理摘录,放到博客上留存。

这本书篇幅非常小,估摸3小时可以就读完。都是软件工程方面的经验之谈,值得读一读。

下面是从Kindle上导出的标注:

简约之美:软件设计之道, Max Kanat-Alexander

《简约之美》其实只强调了几条互相联系的简单道理:

  • 软件是必然要变化的,变化是常态;
  • 有变化就需要维护,随着时间的推移,维护成本会远远超过初期开发的成本,占据成本的大头;
  • 因此,在软件开发中,最重要的是要降低维护成本;
  • 维护成本正比于系统的复杂程度,所以要降低维护成本,系统的设计就应当追求简单清晰。

这根逻辑链条看似简单,其实并非如此。不少有经验的开发人员,似乎对这类“道理”不屑一顾。

软件系统中任何与架构有关的技术决策,以及在开发系统中所做的技术决策,都可以归到“软件设计”的范畴里。

设计程序员能尽可能容易开发和维护的软件系统,这样的系统才能为用户提供尽可能多的帮助,而且能持续提供尽可能多的帮助。

软件设计的本质中的所有问题,都可以用下面的方程式来解答:

 D = V/E

其中:

  • D 表示这个变化的合意程度(可取程度)。我们对此项工作的需求有多么迫切?
  • V 表示它的价值。该变化价值几何?一般来说,你可以问自己“这个变化对用户有多少用”;当然,还有很多其他方法来判断其价值。
  • E 表示完成这个变化的成本,也就是完成它需要付出的代价。

所以,这个等式的意思是:任何一点改变,其合意程度与其价值成正比,与所付出的成本成反比。

这并不是在判断某个变化绝对对错,而是指导你如何分辨并排序你的选项。能带来较大价值、花费成本较少的变化,要比带来较少价值、花费较多成本的变化“更好”。

价值由两部分组成:可能价值(这个变化有多大可能帮到用户)、潜在价值(这个变化在对用户提供帮助的时候,将为用户提供多大的帮助)。

成本包含实现成本和维护成本,价值也包括当前价值和未来价值。用方程式来表示就是:

E = Ei + Em
V = Vn + Vf

其中:

  • Ei 代表实现成本
  • Em 代表维护成本
  • Vn 代表当前价值
  • Vf 代表未来价值

考虑所有因素,完整的方程式就是:

D = ( Vn + Vf ) / ( Ei + Em )

用文字说明就是:改变的合意程度,正比于软件当前价值与未来价值之和,反比于实现成本和维护成本之和。 这就是软件设计的最重要规律。

相比降低实现成本,降低维护成本更加重要。

设计的质量好坏,正比于该系统在未来能持续帮助他人时间的长度。

程序员犯的最常见也是最严重的错误,就是在其实不知道未来的时候去预测未来。

如果完全不考虑未来,只根据当前已知的确切信息确定所有设计决策,那就百分百安全了。 这个说法听起来与本章之前说的相矛盾,其实并非如此。在进行决策时,未来才是最重要的事情。但在进行决策时,考虑未来的变数和尝试预测未来,是有区别的。

在软件设计时,可以根据已知的信息做某些决策,目的是为了创造更好的未来(提升价值,降低维护成本),而不必预测未来究竟会发生什么具体的事情。

「变化定律」(Law of Change):程序存在的时间越久,它的某个部分需要变化的可能性就越高。

关键在于,你并不需要去预测什么会变化,你需要知道的是,变化必然会发生。程序应该保证尽可能合理的灵活性,这样,不管未来发生什么变化,都可以应付得了。

另一点值得学习的有趣之处是,回顾某个特定文件修改历史。如果某个文件存在了很长时间,而且你有程序记录每个文件的修改历史,请回顾整个过程中的每次修改。问问自己,最初写这个文件时,你能预测到这些变化吗,是否一开始写好就能够减轻后期的工作量。总的来说,就是要尝试理解每次修改,看看是否能从中得到一些关于软件开发的新的收获。

为了适应变化定律,软件设计师常常会掉进误区。其中有3个误区最常见,这里按照其发生频率逐一列出来:

  • (1)编写不必要的代码
  • (2)代码难以修改
  • (3)过分追求通用

如今,软件设计中有一条常见的规则,叫做「你不会需要它」(You Ain’t Gonna Need It),或者简称为 YAGNI 。其实这条规则的意思是,不应该在真正的需求来临之前编写那些代码。

其实,这条规则应当这样展开:不要编写不是必需的代码,并且要删除没有用到的代码。 也就是说,你还需要删除所有用不到的代码;如果真的需要,你随时可以恢复回来。

软件项目的一大杀手就是所谓的「僵化设计」(rigid design)。也就是说,程序员写出来的代码很难修改。僵化设计有两大原因: 对未来做太多假设 不仔细设计就编写代码

更好的办法是每次只确定一个或者少数几个需求,然后立刻让开发人员实现它。在开发过程中,用户可以扮演开发人员的角色,反复进行沟通。上次确定的功能实现并发布之后,就可以继续处理其他的功能。这样,最终得到的系统是设计良好的,完全满足用户需求的。

设计程序时,应当根据你现在确切知道的需求,而不是你认为未来会出现的需求。

这不是说不要做规划。在软件设计中,一定程度的规划是非常有价值的。但是,即便不做详细的规划,只要你能保持改变的幅度很小,代码也很容易适应不确定的未来,就没有大的风险。

做一个足够通用的办法,保证(他们自己相信)可以适应未来任何可能的形势。我们称这种做法为“过度工程”(overengineering)。 按照字典的定义,overengineering就是over(意思是“过分了”)加engineer(意思是“设计和构造”)。根据这种解释,过度工程意思就是,在设计或者构造上花了过多的精力。

在追求通用时,应当选择正确的事情,选择正确的方法,这是成功的软件设计的基础。然而,太过通用,会带来说不完的复杂和混乱,也会大大抬高维护成本。避免此误区的办法,和避免僵化设计的一样: 仅仅根据目前确知的需求来考虑通用。

有个办法可从根本上避免这三大误区,这就是「渐进式开发和设计」。它要求按照特定顺序,一点一点地设计和构建系统。

「缺陷概率定律」: 在程序中新增缺陷的可能性与代码修改量成正比。

有时候,该规则也会被非正式地表述为:“如果不新加代码,也不修改代码,就不会产生新缺陷。”

最好的设计,就是能适应外界尽可能多的变化,而软件自身的变化要尽可能少。

永远不要「修正」任何东西,除非它真的是一个问题,而且有证据表明问题确实存在。

在这类问题上,最有名的错误就是所谓的「提前优化」。也就是说,有些开发人员想让速度尽可能快,所以,他们还没弄清楚速度到底慢不慢,就花时间来优化程序。这就好像做慈善事业时,一边把食物送给富人,一边说“我们只是希望帮助他人”。这不合逻辑,对吧?因为这样是在解决根本不存在的问题。

理想情况下,任何系统里的任何信息,都应当只存在一次。

如果既要做很多修改,又希望这些变化不要引入错误,还可以用上另一条法则。它不仅仅用来消除错误,还可以保持程序的可维护性,降低新增功能的难度,让代码更容易理解。这就是「简洁定律」(Law of Simplicity): 软件任何一部分的维护难度,与该部分的简洁程度成反比。 (这块原文和翻译好像有点问题, 我适当修正了下)

用户只希望尽可能简单,这样才能迅速上手,才能真正用起来。 许多程序员在这方面做得尤其差劲。他们以为别人都愿意花很多时间来学习自己写的代码,毕竟这是自己花很多时间写出来的。

名字应当足够长,能够完整表达其意义或描述其功能,但不能太长,以免影响阅读。

复杂性是会叠加的,而且不是简单的线性叠加。

只有在满足以下任何一个条件的前提下,重新发明轮子才有价值:

  • 你需要的东西不存在;
  • 现有的各种“轮子”都很糟糕,会把你困住;
  • 现有的“轮子”根本无法满足你的需求;
  • 现有的“轮子”缺乏良好的维护,而你也不能接过维护的任务(比如,你没有源代码)。

所谓互通性,指的是如果需要,从一种技术切换到另一种技术有多难。

所以,如果事情变复杂,不妨回过头去看看真正要解决的是什么问题。

如果系统中某个部分太过复杂,有个好办法来解决:把它分解成几个独立的小部分,逐步重新设计。每次修改都应该足够小,这样可以放心动手,不会让事情更复杂。不过这个过程中最大的。