软件的熵增现象

一、熵增定律

熵的概念最早起源于物理学,用于度量一个热力学系统的无序程度。热力学第二定律,又称“熵增定律”,表明了在自然过程中,一个孤立的系统总是从最初的集中、有序的排列状态,趋向于分散、混乱和无序;当熵达到最大时,系统就会处于一种静寂状态。

通俗的讲:系统的熵增过程,就是由原始到死亡的过程。“熵”是“活跃”的反义词,代表负能量。

非生命,比如物质总是向着熵增演化,屋子不收拾会变乱,手机会越来越卡,耳机线会凌乱,热水会慢慢变凉,太阳会不断燃烧衰变……直到宇宙的尽头——热寂。

在软件开发、维护过程中。软件的生命力总是从最初的理想状态,逐步趋向于复杂、混乱和无序状态发展,直到软件不可维护而被迫下线或重构。这种损坏软件质量的因素的逐步增长,叫做软件的熵增现象。

 

二、系统复杂性

表象

  • 代码混乱、新人不易上手
  • 代码高度冗余,复用性低,开发效率低
  • 扩展和修改困难,牵一发动全身
  • 业务数据错乱
  • 程序性能低下
  • 系统难以移置
  • BUG率居高不下

深层原因

  • 变更放大
  • 认知负荷
  • 未知的未知

三、复杂性的原因

复杂性是由两件事引起的:依赖性和模糊性。

1、依赖关系

依赖关系是软件的基本组成部分,不能完全消除。实际上,我们在软件设计过程中有意引入了依赖性。每次编写新类时,都会围绕该类的 API 创建依赖关系。但是,软件设计的目标之一是减少依赖关系的数量,并使依赖关系保持尽可能简单和明显。

2、模糊性

当重要的信息不明显时,就会发生模糊。

一个简单的例子是一个变量名,它是如此的通用,以至于它没有携带太多有用的信息(例如,时间)。或者,一个变量的文档可能没有指定它的单位,所以找到它的惟一方法是扫描代码,查找使用该变量的位置。

晦涩常常与依赖项相关联,在这种情况下,依赖项的存在并不明显。例如,如果向系统添加了一个新的错误状态,可能需要向一个包含每个状态的字符串消息的表添加一个条目,但是对于查看状态声明的程序员来说,消息表的存在可能并不明显。

不一致性也是造成不透明性的一个主要原因:如果同一个变量名用于两个不同的目的,那么开发人员就无法清楚地知道某个特定变量的目的是什么。

3、依赖性和模糊性的积累

复杂性不是由单个灾难性错误引起的;它堆积成许多小块。单个依赖项或模糊性本身不太可能显着影响软件系统的可维护性。之所以会出现复杂性,是因为随着时间的流逝,成千上万的小依赖性和模糊性逐渐形成。最终,这些小问题太多了,以至于对系统的每次可能更改都会受到其中几个问题的影响。

四、降低复杂性的方法

1、日常开发留出一点战略规划时间

大多数程序员日常以战术编程的心态来进行软件开发。例如新功能或错误修复。乍一看,这似乎是完全合理的:还有什么比编写有效的代码更重要的呢?但是战术编程几乎不可能产生出良好的系统设计。

与之相对应的是战略规划,成为一名优秀的软件设计师的第一步是要意识到仅工作代码是不够的。尽管代码当然必须工作,但不应将“能跑通的代码”视为主要目标。战略设计的主要目标必须是制作出出色的设计,考虑后续的可维护性及扩展性。

战略性编程需要一种投资心态。尽管前提投入会比战术编程花费更多的时间,但随着系统的迭代,战略编程的优势就开始逐渐显现。

当然既然是投资,就要考虑投入产出比,不应该吹毛求疵,只要发现一点不合理的地方就整体大重构。推荐的方式小步快跑的方式,在日常开发中留出5%-10%的时间来做战略设计。

2、模块的设计

开发一个新模块,如果有不可避免的复杂性。两种设计思路哪个更好:1、应该让模块用户处理复杂性,2、应该在模块内部处理复杂性?如果复杂度与模块提供的功能有关,则第二个答案通常是正确的答案。

作为开发人员,很容易以相反的方式行事:解决简单的问题,然后将困难的问题推给其他人。如果出现不确定如何处理的条件,最简单的方法是引发异常并让调用方处理它。这样的方法短期内会使您的生活更轻松,但它们会加剧复杂性。大多数模块拥有的用户多于开发人员,因此此模块还会有许多人来维护。作为模块开发人员,您应该努力使模块用户的生活尽可能轻松,即使这对您来说意味着额外的工作。另一种更好的方法是,模块具有简单的接口比简单的实现更为重要。

模块是设计应该是深的,最好的模块是那些其接口比其实现简单得多的模块。这样的模块具有两个优点。1、一个简单的接口可以将模块强加于系统其余部分的复杂性降至最低。2、如果以不更改其接口的方式修改了一个模块,则该修改不会影响其他模块。如果模块的接口比其实现简单得多,则可以在不影响其他模块的情况下更改模块的许多方面。

3、如何编写注释

编写注释的原因是,使用编程语言编写的语句无法捕获编写代码时开发人员想到的所有重要信息。注释记录了这些信息,以便后来的开发人员可以轻松地理解和修改代码。注释的指导原则是,注释应描述代码中不明显的内容。

注释的最重要原因之一是抽象,其中包括许多从代码中看不到的信息。抽象的思想是提供一种思考问题的简单方法,但是代码是如此详细,以至于仅通过阅读代码就很难看到抽象。注释可以提供一个更简单,更高级的视图(“调用此方法后,网络流量将被限制为每秒 maxBandwidth 字节”)。即使可以通过阅读代码推断出此信息,我们也不想强迫模块用户这样做:阅读代码很耗时,并且迫使他们考虑很多不需要使用的信息模块。开发人员应该能够理解模块提供的抽象,而无需阅读其外部可见声明以外的任何代码。

4、重视命名

名称是一种抽象形式:名称提供了一种简化的方式来考虑更复杂的基础实体。良好的名字是一种文档形式:它们使代码更易于理解。它们减少了对其他文档的需求,并使检测错误更加容易。相反,名称选择不当会增加代码的复杂性,并造成可能导致错误的歧义和误解。

你可能感兴趣的:(架构设计,程序人生,软件架构,架构)