软件的复杂性与构造定律

  快看!看!深入研究自然,你就会明白一切。 - 爱因斯坦

复杂性是被低估的。复杂越高,开发人员会感到不安。对其的理解认知负荷代价就越高,我们就更不快乐。真正的挑战是在构建我们的系统时要保持其有序以及工程师的生产方式。对于这一点,一个简单的物理规律可以帮助我们:构造定律 the Constructal Law.

当我们拥有臃肿大量的组件和类时,其中每一个又由大量的职责行为功能组成,我们不会在系统层面去思考,相反,我们只是将大量脂肪性质类进行组群,从外表看,你是无法找出这个系统做什么用的,这个系统代表的,业务需求或用户案例是什么。因为我们的应用程序进行了隐藏。

Neil Johnson, 在其a big proponent of Complexity Science中对复杂系统的定义如下:

(复杂的)系统表现出是一种复杂的有序和无序行为的混合。

对于软件来说,这种复杂的系统一般不是偶然发生的,它它是人类人为的结果。是工程师在以有序和可持续的方式构建组件和系统。    

最聪明的人自从60年代开始在计算机科学已经进行了不少最佳实践。然而,这是不够的。虽然我们已经从自然中得到启发解决了我们的不少困难问题,如神经网络、能源发电和液压。令人惊讶的是,当谈到复杂性,自然也有一种复杂的方法来解决这一问题:在有序和无序中平衡。

 

有序和无序

软件设计者应该是分离应该分离事物,而不是将本应一起考虑的事情进行分离。-Kent Beck

当我们决定打破臃肿的组件分离成小和孤立的部分时,我们面临着一个巨大的编程悖论:起初,一切都是彩虹和独角兽。但是后来,缺非如此。也就是说,事情并没有我们起初想得那么简单。

最初,我们第一次努力隔离后,我们会很高兴。一切看起来是有些紧张的和美丽的。然而,很快事情开始分崩离析,我们最终得到的东西比我们最初的组件要差得多。组件和类变得很大复杂了如下图:

那么下面最自然的事情是什么?放弃,回到我们当初的,继续臃肿的类。这样我们会感觉更好,但实际上我们是在倒退。这些组件不是问题。问题是复杂性,它是一个活着的野兽,会尽一切可能增长,你需要学会驯服它。(Quoting Neil Johnson)

(复杂的)系统似乎是"活着"。系统是在一个高度不平凡的且经常复杂的方式演进发展,这是由一个代理的生态驱动着,这些代理相互作用交互,且在对不断的反馈下不断地适应。

复杂性会增加

让我们将系统的复杂性看成是两个组件之间的许多交互,在两个组件情况下,复杂度是1,如下图:

如果增加一个组件,复杂度将从1增加到3:

复杂度以指数级的增长是惊人的,当我们增加到六个组件,复杂度将是15。

显然,这种拓扑可能是一个极端,但却能公平地明复杂性需要驯服。老实说,这个极端的例子并不少见,这正是人们做的事情,复杂性感染一切。什么出错了吗?

构造定律Constructal Law

自然界是如何应对这复杂呢?这在物理中被称为构造定律 Constructal Law, 仅有的我们知晓的大自然是如何指导复杂演化的规律(可以说是上帝创造万物的方式),是由Adrian Bejan于1995创立的构造定律:

For a finite-size system to persist in time (to live), it must evolve in such a way that it provides easier access to the imposed currents that flow through it.

对于一个有限大小的持续活动的系统,它必须以这种方式发展演进:它提供了一种在自身元素之间更容易访问的流动方式。

这个定理在自然中比比皆是,典型的是树的生长,先有根,再有枝,枝上再生枝叶。这种自发性质的设计反映了这一趋势:他们允许实体或事物更容易地流动 - 以最少的能量消耗到达最远的地方,就连街道和道路这些人为地构建物体,往往也是有排序的模式,以提供最大的灵活性。

构造定律致力于描述能量和物质在物理网络(如河流)和生物网络(如血管)中的流动,这个理论提出,如果一个流体系统(flow system)要继续存在(比如,生存),那么他必须始终提供更容易的方式来获得这个系统中的流体。换句话说,系统应该致力于将能量消耗减少到最低限度,而同时将消耗单位能量产生的熵提高到最大限度

Bejan相信,进化实质上是这么一个过程,即生物体不断的重组他们自身,以使能量和物质能够尽可能迅速高效的通过他们。更好的流体结构(flow structure),不管它们是动物还是河流,将取代那些较差的结构。。

构造定律作为第二个时间箭头,将和热力学第二定律一道将宇宙推向无序。

 

用构造定律进行有序化

回到我们这个复杂系统案例,我们可以使用构造定律降低复杂度,增加第7个组件后,将系统复杂度从15降低到8。

为了实现这种方式开发,你需要了解你的对象不是隔离的实体,而是一个大概念的一部分,比如这个大概念是你的webservice 客户端, 由很多对象组成包括连接 授权和XML文本 响应等。另外一个概念是你的数据库组件,它是由许多对象组成,如ORM Active Record类,校验类 连接类或它们之间的关系,这个大概念是一个聚合群组,聚合了很多子对象于其中,是一个集合概念。

下面以一种模块化分离关注是更加有意义:

 

与构造定律共存

在开发,不要让你的直觉欺骗你以至于产生马虎的代码。不要让一个模块的一部分调用另一个模块的内部组成部分。如下图:

 

对于一个组件A和X个组件之间的交互,每一次A的改变将需要1+ x 改变。

组件之间的交互越少,越好,远离扁平,用模块进行封装,我们能确保我们的系统增加时不会被复杂性淹没。

 

英文原文:Complexity in Software 3: The Constructal Law

加法是自然之道

ORM和Rails的问题