《简约之美 软件设计之道》读后感二

发布时间 2023-12-27 23:31:40作者: 布吉岛???

第四章 未来

软件设计师面对的主要问题是:在设计软件时,应该做怎样的决定?面对的众多可能,哪一个才是最好的。不要绝对好坏,而是哪些更好,是个排序问题,我们要做的是从所有可能中选出最好的决定。比如,眼前功能100项,但我们的人力只能够完成2项。应该选哪2项呢?

软件设计的方程式:上面的问题,还包括软件设计的本质中的如有问题,都可以用下面的方程式来解答: D = V/E, expense

其中,D表示这个变化的合意程度(可取程度)。我们对此项工作的需求有多么迫切?

V表示它的价值,几何?对用户多少用,当然还有很对其他方法来判断其价值。

E表示完成这个变化的成本,也就是完成它需要付出的代价。

这个等式的意思是:任何一点改变,其合意程度与其价值成正比,与所付出的成本成反比。是否应当维持不变?价值何在,要花什么成本?

4.1.1 价值

这个变化能带给人多大帮助

价值最简单的定义是:

所有最需要帮助的人里面,最重要的是你的用户。不过,为了赚更多钱而开发某些功能,也是一种形式的价值,对你来说有价值,其实,创造价值的方式有很多种。上面只是举两个例子。

要准确衡量某点变化所创造的价值,有时候是很难得。比如软件帮人减肥,怎么精确衡量帮人减肥的价值?

判断每一点可能的变化的价值,基本的依据是开发人员的开发经验,还包括对用户做恰当的研究,找到对他们帮主最大的工作。

1,价值可能性和潜在价值

多少用户(占多大比例)会从此项工作中受益?

此功能对用户有价值的可能性有多大?或者换个说法:此功能发挥价值的频率有多高?

在它发挥价值的时候,它能发挥出多大的价值?

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

举例来说,某个功能可以延长人的寿命,可能性很低,但仍然非常有用。

所以,在判断价值时,你应该考虑:

2,平衡危害

有些改变在带来帮助的同时也会带来麻烦。如果你的程序展示广告,提供资金VS觉得烦。

权衡改变的价值,需要衡量它可能造成的危害,并权衡利弊。

3,赢得用户的价值

如果某个功能找不到用户,就不存在实际的价值。可能用户找不到或者很难使用,或者根本帮不上任何人。在将来它们可能有价值,但目前没有。

这也意味着,大多数情况下,为了确保你的软件有价值,就必须真正发布它。如果某个变化花费的时间太长,最后可能就没有任何价值,因为它不能及时发布,无法给人提供切实的帮助。评估发布计划是非常重要的。

4.1.2 成本

比起价值,成本更容易量化一点。通常,你可以计算“需要多少个人工作多少小时”。

尽管成本可以量化,落实起来却相当复杂,甚至是不可能的。有些变化包含隐形成本,难于预测,比如花在修正开发某项功能造成的bug上的时间久很难预测。

不过,经验丰富的可以不需要知道确切数值的情况下,根据可能成本来预测。

预测某个变化的成本时,重要的是要考虑牵涉到的所有投入,而不仅仅是编程的时间。研究,沟通,思考,花多少时间。简单说,与这个变化有关的每一点时间,都是成本的一部分。

4.1.3 维护

目前,这个方程式很简单,但忽略了一个重要的因素--时间。完成,并要维护。

比如,报税软件,每年新规定公布后要维护。即使暂时看不到长期的维护成本,即使维护成本只是你下一年必须再做一次测试,维护成本依然存在。

还必须考虑现在价值和未来价值。修改现有的系统时,受益的是现在的用户,但是它也可能帮助到未来的用户,甚至影响未来用户的数目,进而影响到现有系统究竟总共能帮助到多少人。

有些功能的价值会随时间变化,贬值或增值。

成本包括实现成本和维护成本,价值也包括当前价值和未来价值。用方程式来表示就是:E=Ei+Em, V=Vn + Vf

4.1.4 完整的方程式

考虑所有因素,完整的方程式就是: D = (Vn + Vf)/ (Ei + Em)

用文字说明就是:改变的合意程度(可行性),正比于软件当前价值与未来价值之和,反比于实现成本和维护成本之和。

4.1.5 化简方程式

未来价值和维护成本都取决于时间,如果把这个方程式应用到现实中,随着时间的改变,会出现非常有意思的现象。为说明这个问题,假设价值和成本都可以用金钱来衡量。带来或需要投入多少钱。D = ($10000 + $1000/天)/($1000 + $100/天),随着时间的流逝,最开始说的当前价值和维护成本看来就微不足道了。

变成了D = Vf / Em

几乎所有软件设计决策都完全忽略了未来价值与维护成本的对比。有时候当前价值和实现成本足够大,始终在决策中占有决定性地位,但这种情况很少见。长期受益和维护成本才是真正需要考虑的,与之相比,当前价值和实现成本变得无足轻重。

4.1.6 你需要什么,不需要什么

避免某项工作维护成本最终大大超过未来的收益。

可以用微积分,把这个方程式理解为一个具有极限的无穷数列,而不是某个静态的方程式。

只要维护成本的增长速度比价值快,就会遇到麻烦。

理想的解决方案,也即保证成功的唯一途径——就是这样设计你的系统:保证维护成本随时间降低,最终降到零(或者尽可能接近零)。只要你能做到这点,就无所谓未来收益是大还是小,总之你不需要关心。

能带来更高未来价值的改变仍然是更可取的,不过,只要每项决策的维护成本都会随时间降低到接近于零,未来你就不会陷入危险的境地。

只要未来收益超过维护成本,也是值得做的。

相比降低实现成本,降低维护成本更加重要。(也许实现上花很高成本,做相当多的设计和规划)

维护成本从哪里来?怎样设计系统,才能保证维护成本会随时间推移而降低?要对未来做更细致的检查。

4.2 设计的质量

如今,要写出一款能帮助某个人的软件,实在是太容易了。但是,要写出能帮助几百万人,而且是在未来几十年里持续提供这种帮助的软件,则要难的多。那么,编程中的大部分精力应该花在哪里?大多数人什么时候使用这款软件?现在,还是未来的几十年?

答案是,相比现在,将来有多得多的编程工作要做,也有更多的用户要帮助。在未来,你的软件必须保持竞争力,才能继续存在,同时,维护成本和用户数量也会增加。

于是,得到这条规律:设计的质量好坏,正比于该系统在未来能持续帮他人时间的长度。

如果你的软件只能在未来几小时内提供帮助,就不需要花太多的功夫去设计。

不要把自己禁锢在某种工作定势里,要保持灵活;不要做任何以后无法改变的决策;在设计时要慎重,慎重,再慎重。

4.3 不可预测的结果

未来的某些事情,是我们所不知道。

设计软件时最应该关注的是未来,不过,关于任何工程,都有一点极为重要:

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

比如1985年程序员写了一个修复软盘错误的程序,他预期是“人们会一直使用软盘”,但现在没用了。

预测短期未来是可能的,但长期未来基本是未知的。但相比短期,长期的未来对我们来说更重要,因为我们的设计决策会在未来更长的时间里产生更大的影响。

如果完全不考虑未来,只根据当前已知的确切信息确定所有设计决策,那就百分百安全了。

在进行决策时,未来才是最重要的事情。但在进行决策时,考虑未来的变数和尝试预测未来,是有区别的。

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

也有少量例外,有时候你确切知道未来短期内会发生什么,便可以据此决策。如果这么做,必须对未来有相当的把握。无论你多聪明,肯定没有简单的办法准确预测长期未来。

简单来说,CD诞生于1979年,最初设想是取代磁带,成为最主要的听音媒质。出现DVD到计算机的兼容CD/DVD的驱动器,以及CD-ROM以50倍速读取CD出先什么问题,谁会想到。

正因如此,任何工程,包括软件开发,都有指导原则。重要的是要记住,存在着未来。

某条特定的规则和条例在未来会以什么样的方式帮助你也无法预测,但它确实能帮上忙,而且你会欣然乐意把它们应用在工作中。

第 5 章 变化

程序存在的时间越久,它的某个部分需要变化的可能性就越高。

未来有些东西是我们不知道也不可能知道的,但可以确定的一点,随着时间的流逝,软件所处的环境会变化。没有东西可以永恒不变。

也就是说,软件必须随环境变化而变化,才能适应所处的环境。

于是,我们得到了变化定律: law of change

未来是无穷无尽的,所以可以百分百地肯定,最终,程序的每个部分都必须变化。

哪些部分会发生变化,为什么变化很难预测。

可能你的程序是为4轮轿车写的,可未来每个人都开着18个轮的大卡车;可能你的程序是写给高中生的,但是高中教育的质量差到没有学生懂你的程序。

关键不需预测变化,应保证尽可能合理的灵活性。应付变化。

5.1 真实世界中程序的变化

看一份关于真正的程序随时间流逝而变化的数据吧,某个程序数百个文件,4个样本的变化详情。

文件1 文件2 文件3 文件4
分析时长
初始行数
未变化行数
当前行数
增长数
变化次数
新增行数
删除行数
修改行数
总变化行数
变化率 1.6x 8.9x 13x 36x
行数,包含文件的每一行:代码,注释,文档,空行。未变化的行几乎都是注释,文档,空行。

观察变化率可以发现,修改文件的工作量比最初写文件时的还要大。当然,行数这个指标不能准确反映工作量,但是它可以体现一般趋势。

总变化行数通常比当前行数要大。改的多,不是新增代码。

关于这些数字,还可以做其他许多有趣的分析。鼓励探究这些数据,或分析自己项目里的类似指标。

若是有全部修改历史,回顾修改的每次,和最初写这个文件相比,能预测到这些变化,或减轻后期的工作量。尝试理解每次修改,看是否能从中得到一些关于软件开发的新收获。

5.2 软件设计的三大误区

1,编写不必要的代码

2,代码难以修改

3,过分追求通用

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

5.2.1 编写不必要的代码

如今,软件设计中有一条常见的规则,叫做“你不会需要它”(You Ain't Gonna Need It),或者简称YAGNI。

不要编写不是必需的代码,并且要删除没有用到的代码。

如果真的需要,你随时可以恢复回来。

有些人相信自己可以抗拒变化定律,保留暂时用不到的代码,现在就编写一些今后才会需要的代码。

5.2.2 代码难以修改

1,对未来做太多假设

2,不仔细设计就编写代码

软件项目的一大杀手就是所谓的“僵化设计”(rigid design)。

僵化设计有两大原因:

医院的错误在于试图预测未来,花了1年时间来写系统实现细节文档。

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

Status字段,记录当前任务的进度,No Work Done, In Progress, On Hold, Complete.

类似的部分分得很散,没有注释,没有逻辑,每次要修改时,除了阅读整个文件外别无他法。

避免僵化设计,就应当做到:设计程序时,应当根据你现在确切知道的需求,而不是你认为未来会出现的需求。

保持每次改变的幅度都很小,设计的难度也会很小。

5.2.3 过分追求通用

既然代码将来要修改是一个事实,有些开发人员给出的应对方案就是:做一个足够通用的办法,保证可以适应未来任何可能的形式。

我们称这种做法为过度工程,overengineering。

Engineer,意思是设计和构造。

问题:1,都不够满足未来需求,2,无法满足具体的需求,3,写很多不需要的代码

在追求通用时,应当选择正确的事情,选择正确的方法,这是成功的软件设计的基础。

仅仅根据目前确知的需求来考虑通用。

5.3 渐进式开发及设计

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

这个方法的精妙之处就在于,它是根据实现的顺序来决策的。总的来说,在其中的每个阶段,下一步都只做最容易的事情。

先选加法,因为是最简单的运算,其次选择减法,因为从逻辑上说,它与加法只有很小的差异。也可以选择乘法,是把加法执行很多次。不选除法,从加法到除法的步子距离太远了。最后一步,从乘法到除法。

有些时候,你甚至需要把某个单独的功能拆分为一系列小的,简单的逻辑步骤,然后才可以很方便地实现。

混合了两种做法,一种叫渐进开发,一种叫渐进设计。渐进开发是一种通过小步骤构建整个系统的办法。先实现,在修改设计,再实现,再修改设计。

渐进开发和设计并不是唯一有效的软件开发方法,但是它无疑可以避免之前列出的三大误区。

第6章 缺陷与设计

没有哪个程序员可以不犯错误,最优秀的程序员,在状态最好的时候,每写1000行代码也会犯一个错误。

不变得:写的代码越多,引入的缺陷就越多。

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

错误妨碍了我们帮助他人的目标,应当避免。修复缺陷也是维护工作的一种,所以新增缺陷还会抬高维护成本。

小的变化 = 更少的缺陷 = 更少的维护

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

这规律似乎与“变化定律”相矛盾。

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

6.1 如果这不是问题

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

可能用户没弄清楚到底要干什么。

最有名的错误就是:提前优化

在你的程序中,真正需要关注速度的部分,应该局限于你可以证明的,真正让用户体会到有性能问题的那些部分。对程序的其他部分,最主要关心的还是灵活和简洁,而不是速度。

6.2 避免重复

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

比如用户界面上的东西,若是硬编码,要改很多地方。

对代码段也同样适用。我们不应该复制粘贴代码段,相反,应该使用各种编程技巧来处理,让各处的代码可以“使用” use,“调用” call,“包含” include已有的其他代码。

一个强有力的理由,就是缺陷概率定律。重用,引入错误少。