背景
信息和计算领域,有很多悬而未决的问题,归约到软件系统或者应用技术领域,复杂度是最为显著和重要的问题之一,它关联着产品、技术、工具、文档、组织等许多方面的现状和历史,又深远密切地影响者它们的未来。能否解决软件复杂度带来的问题,控制好软件复杂度的增长速度,是我们能否多快好省地落实产品计划,实现产品需求,提升产品性能的核心所在。
接触过程序设计的人都了解一个概念,程序=数据结构+算法,其中算法又有时间复杂度和空间复杂度,但是稍有经验者会知道,算法复杂度属于计算复杂度,无法用于评估庞大软件系统,也无法用于指导更加复杂的研发过程。对此,我们衍生出的软件工程,以及其他如系统论、控制论、耗散论、协同学等无关的基础学科,有着更加清晰的问题阐述,更加合适的理论框架,和更加有效的解决方案。但是它们离应用技术领域有稍有距离且难以掌握,我们往往用自己熟悉的语言,用经验性方法去交流、解决软件复杂度带来的问题。显然,很多时候,我们无法将面临的问题,归纳到统一的逻辑框架中,得出始终一致的,有效的结论和方案。
因此,我想借助本文,提出一个简要的复杂度评估模型,用于指导在应用技术领域的架构、设计、重构等工作,并倡导一个思想——将复杂度控制,作为软件迭代的基础目标。为了引申出,并且更好地理解这个模型,我们先来了解一下,关于(软件)复杂度,目前的一些研究和看法。
软件工程中的复杂性定义
软件工程学科也没有提出通用的复杂性定义和度量方式,但针对某些具体的场景,有一些明确的定义和评估方式。
Manny Lehman的复杂度定义
Manny Lehman教授在软件演进法则中首次系统性提出了软件复杂度。
软件(程序)复杂度是软件的一组特征,它由软件内部的相互关联引起。随着软件的实体(模块)的增加,软件内部的相互关联会指数式增长,直至无法被全部掌握和理解。
在他的描述中,软件复杂度的影响是:
软件的高复杂度,会导致在修改软件时引入非主观意图的变更的概率上升,最终在做变更的时候更容易引入缺陷。在更极端的情况下,软件复杂到几乎无法修改。
Halstead的复杂度定义
Halstead 复杂度 (Maurice H. Halstead, 1977) 是软件科学提出的第一个计算机软件的分析“定律”,用以确定计算机软件开发中的一些定量规律。Halstead 复杂度采用一组基本的度量值,这些度量值通常在程序产生之后得出,或者在设计完成之后进行估算。
Halstead 复杂度根据程序中语句行的操作符和操作数的数量计算程序复杂性。
- 操作符和操作数的量越大,程序结构就越复杂。
- 操作符通常包括语言保留字、函数调用、运算符,也可以包括有关的分隔符等。
- 操作数可以是常数和变量等标识符。
John Ousterhout的复杂度定义
斯坦福教授、Tcl语言发明者John Ousterhout,在他的著作《A Philosophy of Software Design》中提出,软件设计的核心在于降低复杂性。他选择从认知的负担和开发工作量的角度来定义软件的复杂性,并且给出了一个复杂度量公式:
子模块的复杂度Cp乘以该模块对应的开发时间权重值tp,累加后得到系统的整体复杂度C。系统整体的复杂度并不简单等于所有子模块复杂度的累加,还要考虑开发维护该模块所花费的时间在整体时间中的占比(对应权重值tp)。也就是说,即使某个模块非常复杂,如果很少使用或修改,也不会对系统的整体复杂度造成大的影响。子模块的复杂度Cp是一个经验值,它关注几个现象:
- 修改扩散,修改时有连锁反应。
- 认知负担,开发人员需要多长时间来理解功能模块。
- 不可知(Unknown Unknowns),开发人员在接到任务时,不知道从哪里入手。
造成复杂的原因一般是代码依赖和晦涩(Obscurity)。其中,依赖是指某部分代码不能被独立地修改和理解,必定会牵涉到其他代码。代码晦涩,是指从代码中难以找到重要信息。
McCabe推出的一系列复杂度
70年代,软件系统已经变得极其复杂,无论是开发还是维护都是一项成本高昂的工作。人们意识到必须使软件模块化,以便于开发、测试和维护。为此,成立于1976的McCabe&Associates公司开发出了McCabe Cyclomatic Complexity Metric(圈复杂度)技术对软件进行结构测试,随后又推出了其他一些复杂度,统称为McCabe复杂度。McCabe复杂度包括:圈复杂度、基本复杂度、模块设计复杂度、设计复杂度、集成复杂度、行数、规范化复杂度、全局数据复杂度、局部数据复杂度、病态数据复杂度。
McCabe复杂度是对软件结构进行严格的算术分析得来的,实质上是以图论为工具,对程序拓扑结构复杂性的度量。
它认为程序的复杂性很大程度上取决于程序图的复杂性。单一的顺序结构最为简单,循环和选择所构成的环路越多,程序就越复杂。因此,计算McCabe系列的复杂度时,需要将程序还原成程序图。
程序图是退化的程序流程图。也就是说,把程序流程图的每一个处理符号都退化成一个结点,原来连接不同处理符号的流线变成连接不同结点的有向弧,这样得到的有向图就叫做程序图。程序图仅描述程序内部的控制流程,完全不表现对数据的具体操作分支和循环的具体条件。因此,它往往把一个简单的IF语句与循环语句的复杂性看成是一样的,把嵌套的IF语句与CASE的复杂性看成是一样的。
下面,我们简单了解一下各个复杂度的定义和计算方法。
- 圈复杂度(Cyclomatic complexity,V(G))
圈复杂度也称为条件复杂度,它可以用来衡量一个模块判定结构的复杂程度。其数量上表现为独立路径的条数,也可理解为覆盖所有的可能情况最少使用的测试用例个数。
计算方法:
- 点边计算法,V(G) = E - N + 2
- 节点判定法,V (G) = P + 1
程序的可能错误和高的圈复杂度有着很大关系,圈复杂度最高的模块和方法,其缺陷个数也可能最多。
- 基本复杂度(Essential Complexity, VE(G))
基本复杂度是用来衡量程序非结构化程度的,非结构成分降低了程序的质量,增加了代码的维护难度,使程序难于理解。因此,基本复杂度高意味着非结构化程度高,难以模块化和维护。实际上,消除了一个错误有时会引起其他的错误。
计算方法:
将圈复杂度图中的结构化部分简化成一个点,计算简化以后流程图的圈复杂度就是基本复杂度。
- 模块设计复杂度(Module Design Complexity (iv(G))
模块设计复杂度用来衡量模块之间的调用关系,复杂度越高,模块之间耦合性越高,越难隔离,维护和复用。
计算方法:
从模块控制流图中移去那些不包含调用子模块的判定和循环结构后得到的圈复杂度。模块设计复杂度通常远小于圈复杂度。
- 设计复杂度(Design Complexity (S0)
设计复杂度以数量来衡量程序模块之间的相互作用关系,它提供了系统级模块设计复杂度的概况,有助于衡量进行自底向上集成测试的效果,而且提供了全面衡量程序设计规格和复杂度的数据,不反映独立模块的内部情况。高设计复杂度的系统意味着系统各部分之间有着复杂的相互关系,这样系统将难以维护。
计算方法:
程序中所有模块设计复杂度之和。
- 集成复杂度(Integration Complexity (S1) )
集成复杂度是为了防止错误所必须进行的集成测试的数量表示,另一种说法是程序中独立线性子树的数目。一棵子树是一个有返回的调用序列。就像圈复杂度是测试路径的数目,而集成复杂度是程序或其子系统的独立线性子树。
计算方法:
S1=S0-N+1, N是程序中模块的数目。
- 行数(Number of Lines (nl) )
- 规范化复杂度(Normalized Complexity (nv) )
规范化复杂度等于圈复杂度除以行数
计算方法:
nv=v(G)/nl
复杂性科学中的定义
系统复杂性有专门的学术分支,而软件系统正是一种特定的易于研究的系统,因此,复杂性学科的许多结论和方法,都适用于软件系统,甚至比软件工程中的描述更加通用。
复杂性学科认为,复杂系统是指相互依赖,每个组件的行为依赖其他组件的行为,减少部分或者分解后不能运行的系统。它以本体论和认知论为框架,将复杂性分为以下几类:
组分复杂性
组分复杂性是指,复杂系统拥有数目繁多的组分,组分间拥有多样且复杂的相互作用,要素与要素间的关系呈现出各种不确定。它包括:
构成复杂性
系统演化过程中,构成系统的不同因素会影响其自身的复杂性。
分类复杂性
组分个体要素之间的变异,及其在空间分布上的不规则性,以及由于组分的异质,导致组分种类姿态万千而引起的系统复杂性。
规模复杂性
单元数量越大,单元类型越多,系统则因自身规模的增大而复杂。
结构复杂性
复杂性会随关联结构的从属性和多样性的提高,以及联结数量和强度的提高而增加,整合生成结构复杂性。它包括:
组织复杂性
组织形态复杂度的提高,带来了组织结构的多样性和复杂性,开放系统在演化过程中,结构状态的横向、纵向和空间分布的差异性越大,系统复杂性越高。
层次复杂性
系统不同层级间的作用差别很大,构成系统的子系统的层次越多,系统结构越复杂。
过程复杂性
在复杂系统进化和演化过程中,系统内部的要素之间的相互作用、系统与环境边界交互作用、及系统与外部环境的复杂作用都会产生复杂性。系统通过自组织、耗散行为和自组织临界,不断变革内部结构和外部环境之间的关系,可能会出现分岔、混沌等现象因而会在演化过程中产生复杂性。
功能复杂性
功能复杂性是指系统中要素的平衡性与系统整体功能(结构)之间的关系带来的复杂性。它包括:
预测复杂性
当预测系统状态演化时,复杂性可定义为系统状态或行为的不可预测性。
保持复杂性
当希望保持系统功能时,复杂性可定义为系统的功能,关于系统要素平衡程度的灵敏性(脆弱性或非鲁棒性)。
调控复杂性
当改变系统功能时,复杂性可定义为如何实现新功能或所需功能的难度。
如何根据功能,对系统的要素进行合理的分配,将会直接影响到系统的功能复杂性。从控制论的角度的角度看,系统的复杂性与系统的能控性、可观性或能达性均密切相关,系统设计必须平衡系统性能和复杂性之间的关系。
描述复杂性
从描述系统的状态的工作量、信息量以及存储量角度出发,来定义系统的复杂性。描述复杂性是以数学的复杂性理论和信息论为形式表现出来的,认为系统的复杂性就是描述系统特征的复杂性。
计算复杂性
等效于由计算机解决一个问题,所消耗的时间和空间度量。
算法复杂性
问题解决过程中,涉及的描述、步骤、方法、以及仿真程序等要素的无规则和随机性带来的复杂性。
有效复杂性
以对系统规律性认知的表述长度来衡量系统的复杂性。
复杂性分类的联系
在三维空间中,该分类可以看作是以基元、功能维、结构维为基准,以描述复杂性为手段体现具
体表示过程来定义系统的复杂性,如图:
复杂性分类及联系示意图
复杂性的度量
即使在复杂性学科中,度量方式也并未统一,针对不同的类别的复杂性,有着多种不同的度量方式,有些精确的,有些是经验的,但都相对复杂。鉴于我们的讨论目的以及时间成本,此处不再予以展开。多数时候,我们只需要了解几种复杂度的概念、机制、模糊的度量方案,用于引导我们解决问题的思路,或者比较几种实现方案的复杂度优劣。
复杂度定义整理
了解了软件工程和复杂性科学中的复杂度定义和度量方式之后,我们对复杂度的研究情况,有了整体上的印象。可以看到,复杂度本身的衡量也是趋于复杂的。我们研究复杂度,是为了利用它解决我们的问题,而不是为了多出一个问题。所以,我们简单总结一下,每种复杂度的定义的优缺点。
作为最早提出的复杂度,它是一种经验性的表述,没有度量方式,仅指出了复杂度由软件的规模和内部的关联引起,并且会导致难以理解和修改。
使用程序程序的操作符、操作数作为复杂度的度量方式,本质上,主要是度量程序规模引起的复杂度,是一种模糊的局限性较强的度量方式。
首次在软件领域,提出了使用认知难度和工作量来衡量复杂度,但是它提出的评估方法依然是模糊的,更关键的是,是后验的、总结式的,无法在应用在设计和架构阶段。
基于图论来计算程序复杂度,推出了核心的圈复杂度,以及基于圈复杂度的多个辅助指标,并且开发了统计工具。因为采取图论作为理论工具,接近程序本质,而且理论成熟,因此是一种准确的清晰的复杂度定义,被广泛接受和应用。但是也因为如此,McCabe系列的复杂度都偏向于代码细节,更适合用作函数、类级别的复杂度评估,而作为软件系统复杂度评估手段时,统计会非常困难,理解统计结果,得出有效结论也会非常困难,往往会陷入复杂度评估陷阱,甚至本末倒置。
作为复杂性方向的学科,最系统地剖析了复杂性的本质,且引入了三维空间来衡量系统的复杂度,引入描述复杂度来粘合空间系中的复杂度分量和作为最终复杂度的定量。了解了复杂度科学之后,我们很容易联想、理解软件工程中的复杂度概念。比如,软件难于修改,实际是表达调控复杂度;圈复杂度,实际是表达预测复杂度;计算机科学中的算法复杂度属于描述复杂度等等。
纵观已有的复杂度体系,笔者看来,仍有一些问题尚未解决。例如:
- 软件工程的复杂度概念贴近应用技术领域,但不够系统。复杂性学科中的定义虽然完善,但其中的概念偏于通用和抽象,难以直接用于应用技术领域问题的讨论和判定。
- 所有的复杂度,都未将目标系统的需求(外部变更)和组织考虑为复杂度因素,导致复杂度控制会沦为滞后性的思维和措施,且难以落实到组织行为。
- 多数的复杂度解析,缺少形式化描述和推理过程,导致复杂度理解和讨论,也是一件复杂的事情。
- 未提出复杂度的优化策略,并给出系统性证明。
复杂度的形式化定义
在展开软件系统复杂度形式化之前,我们先讨论抽象系统中,依赖关系带来的复杂度如何评估。
抽象依赖的复杂度计算
设有集合
%22%20aria-hidden%3D%22true%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-58%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-3D%22%20x%3D%221130%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-7B%22%20x%3D%222186%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(2687%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMAIN-31%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%223713%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(4158%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMAIN-32%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%225185%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2E%22%20x%3D%225630%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2E%22%20x%3D%226075%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2E%22%20x%3D%226520%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%226965%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(7410%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-6E%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-7D%22%20x%3D%228507%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%3C%2Fsvg%3E#card=math&code=X%20%3D%20%5C%7B%20x_1%2C%20x_2%2C%20…%2C%20x_n%20%5C%7D&id=lNA8n)
对于 存在多种不同的依赖关系,
记作
由此可得集合上的依赖关系表达式为:
由此(1.1)可知,集合的依赖数量取决与两大维度,一个是元素数量,一个是元素间的依赖关系,彼此独立增长。
现构造图以中的元素为节点,对于每一个依赖关系,画一条异质(即两个节点间允许存在多条)有向边,可知为有向图。为了使得中的依赖关系,符合软件系统的依赖特征,我们规定为起始节点,它不被任何元素依赖,且任意元素,都至少存在一条从出发的路径。即对于
%3D%5Cemptyset%2C%20D(x1%2Cx_i)%20%5Cneq%20%5Cemptyset%3C%2Ftitle%3E%0A%3Cdefs%20aria-hidden%3D%22true%22%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-2200%22%20d%3D%22M0%20673Q0%20684%207%20689T20%20694Q32%20694%2038%20680T82%20567L126%20451H430L473%20566Q483%20593%20494%20622T512%20668T519%20685Q524%20694%20538%20694Q556%20692%20556%20674Q556%20670%20426%20329T293%20-15Q288%20-22%20278%20-22T263%20-15Q260%20-11%20131%20328T0%20673ZM414%20410Q414%20411%20278%20411T142%20410L278%2055L414%20410Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMATHI-78%22%20d%3D%22M52%20289Q59%20331%20106%20386T222%20442Q257%20442%20286%20424T329%20379Q371%20442%20430%20442Q467%20442%20494%20420T522%20361Q522%20332%20508%20314T481%20292T458%20288Q439%20288%20427%20299T415%20328Q415%20374%20465%20391Q454%20404%20425%20404Q412%20404%20406%20402Q368%20386%20350%20336Q290%20115%20290%2078Q290%2050%20306%2038T341%2026Q378%2026%20414%2059T463%20140Q466%20150%20469%20151T485%20153H489Q504%20153%20504%20145Q504%20144%20502%20134Q486%2077%20440%2033T333%20-11Q263%20-11%20227%2052Q186%20-10%20133%20-10H127Q78%20-10%2057%2016T35%2071Q35%20103%2054%20123T99%20143Q142%20143%20142%20101Q142%2081%20130%2066T107%2046T94%2041L91%2040Q91%2039%2097%2036T113%2029T132%2026Q168%2026%20194%2071Q203%2087%20217%20139T245%20247T261%20313Q266%20340%20266%20352Q266%20380%20251%20392T217%20404Q177%20404%20142%20372T93%20290Q91%20281%2088%20280T72%20278H58Q52%20284%2052%20289Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMATHI-69%22%20d%3D%22M184%20600Q184%20624%20203%20642T247%20661Q265%20661%20277%20649T290%20619Q290%20596%20270%20577T226%20557Q211%20557%20198%20567T184%20600ZM21%20287Q21%20295%2030%20318T54%20369T98%20420T158%20442Q197%20442%20223%20419T250%20357Q250%20340%20236%20301T196%20196T154%2083Q149%2061%20149%2051Q149%2026%20166%2026Q175%2026%20185%2029T208%2043T235%2078T260%20137Q263%20149%20265%20151T282%20153Q302%20153%20302%20143Q302%20135%20293%20112T268%2061T223%2011T161%20-11Q129%20-11%20102%2010T74%2074Q74%2091%2079%20106T122%20220Q160%20321%20166%20341T173%20380Q173%20404%20156%20404H154Q124%20404%2099%20371T61%20287Q60%20286%2059%20284T58%20281T56%20279T53%20278T49%20278T41%20278H27Q21%20284%2021%20287Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-2C%22%20d%3D%22M78%2035T78%2060T94%20103T137%20121Q165%20121%20187%2096T210%208Q210%20-27%20201%20-60T180%20-117T154%20-158T130%20-185T117%20-194Q113%20-194%20104%20-185T95%20-172Q95%20-168%20106%20-156T131%20-126T157%20-76T173%20-3V9L172%208Q170%207%20167%206T161%203T152%201T140%200Q113%200%2096%2017Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-30%22%20d%3D%22M96%20585Q152%20666%20249%20666Q297%20666%20345%20640T423%20548Q460%20465%20460%20320Q460%20165%20417%2083Q397%2041%20362%2016T301%20-15T250%20-22Q224%20-22%20198%20-16T137%2016T82%2083Q39%20165%2039%20320Q39%20494%2096%20585ZM321%20597Q291%20629%20250%20629Q208%20629%20178%20597Q153%20571%20145%20525T137%20333Q137%20175%20145%20125T181%2046Q209%2016%20250%2016Q290%2016%20318%2046Q347%2076%20354%20130T362%20333Q362%20478%20354%20524T321%20597Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-3C%22%20d%3D%22M694%20-11T694%20-19T688%20-33T678%20-40Q671%20-40%20524%2029T234%20166L90%20235Q83%20240%2083%20250Q83%20261%2091%20266Q664%20540%20678%20540Q681%20540%20687%20534T694%20519T687%20505Q686%20504%20417%20376L151%20250L417%20124Q686%20-4%20687%20-5Q694%20-11%20694%20-19Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMATHI-6E%22%20d%3D%22M21%20287Q22%20293%2024%20303T36%20341T56%20388T89%20425T135%20442Q171%20442%20195%20424T225%20390T231%20369Q231%20367%20232%20367L243%20378Q304%20442%20382%20442Q436%20442%20469%20415T503%20336T465%20179T427%2052Q427%2026%20444%2026Q450%2026%20453%2027Q482%2032%20505%2065T540%20145Q542%20153%20560%20153Q580%20153%20580%20145Q580%20144%20576%20130Q568%20101%20554%2073T508%2017T439%20-10Q392%20-10%20371%2017T350%2073Q350%2092%20386%20193T423%20345Q423%20404%20379%20404H374Q288%20404%20229%20303L222%20291L189%20157Q156%2026%20151%2016Q138%20-11%20108%20-11Q95%20-11%2087%20-5T76%207T74%2017Q74%2030%20112%20180T152%20343Q153%20348%20153%20366Q153%20405%20129%20405Q91%20405%2066%20305Q60%20285%2060%20284Q58%20278%2041%20278H27Q21%20284%2021%20287Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMATHI-44%22%20d%3D%22M287%20628Q287%20635%20230%20637Q207%20637%20200%20638T193%20647Q193%20655%20197%20667T204%20682Q206%20683%20403%20683Q570%20682%20590%20682T630%20676Q702%20659%20752%20597T803%20431Q803%20275%20696%20151T444%203L430%201L236%200H125H72Q48%200%2041%202T33%2011Q33%2013%2036%2025Q40%2041%2044%2043T67%2046Q94%2046%20127%2049Q141%2052%20146%2061Q149%2065%20218%20339T287%20628ZM703%20469Q703%20507%20692%20537T666%20584T629%20613T590%20629T555%20636Q553%20636%20541%20636T512%20636T479%20637H436Q392%20637%20386%20627Q384%20623%20313%20339T242%2052Q242%2048%20253%2048T330%2047Q335%2047%20349%2047T373%2046Q499%2046%20581%20128Q617%20164%20640%20212T683%20339T703%20469Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-28%22%20d%3D%22M94%20250Q94%20319%20104%20381T127%20488T164%20576T202%20643T244%20695T277%20729T302%20750H315H319Q333%20750%20333%20741Q333%20738%20316%20720T275%20667T226%20581T184%20443T167%20250T184%2058T225%20-81T274%20-167T316%20-220T333%20-241Q333%20-250%20318%20-250H315H302L274%20-226Q180%20-141%20137%20-14T94%20250Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-31%22%20d%3D%22M213%20578L200%20573Q186%20568%20160%20563T102%20556H83V602H102Q149%20604%20189%20617T245%20641T273%20663Q275%20666%20285%20666Q294%20666%20302%20660V361L303%2061Q310%2054%20315%2052T339%2048T401%2046H427V0H416Q395%203%20257%203Q121%203%20100%200H88V46H114Q136%2046%20152%2046T177%2047T193%2050T201%2052T207%2057T213%2061V578Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-29%22%20d%3D%22M60%20749L64%20750Q69%20750%2074%20750H86L114%20726Q208%20641%20251%20514T294%20250Q294%20182%20284%20119T261%2012T224%20-76T186%20-143T145%20-194T113%20-227T90%20-246Q87%20-249%2086%20-250H74Q66%20-250%2063%20-250T58%20-247T55%20-238Q56%20-237%2066%20-225Q221%20-64%20221%20250T66%20725Q56%20737%2055%20738Q55%20746%2060%20749Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-3D%22%20d%3D%22M56%20347Q56%20360%2070%20367H707Q722%20359%20722%20347Q722%20336%20708%20328L390%20327H72Q56%20332%2056%20347ZM56%20153Q56%20168%2072%20173H708Q722%20163%20722%20153Q722%20140%20707%20133H70Q56%20140%2056%20153Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-2205%22%20d%3D%22M331%20696Q335%20708%20339%20722T345%20744T350%20759T357%20769T367%20772Q374%20772%20381%20767T388%20754Q388%20746%20377%20712L366%20673L378%20661Q460%20575%20460%20344Q460%20281%20456%20234T432%20126T373%2027Q319%20-22%20250%20-22Q214%20-22%20180%20-7Q168%20-3%20168%20-4L159%20-33Q148%20-71%20142%20-75Q138%20-78%20132%20-78Q124%20-78%20118%20-72T111%20-60Q111%20-52%20122%20-18L133%2021L125%2029Q39%20111%2039%20344Q39%20596%20137%20675Q187%20716%20251%20716Q265%20716%20278%20714T296%20710T315%20703T331%20696ZM276%20676Q264%20679%20246%20679Q196%20679%20159%20631Q134%20597%20128%20536T121%20356Q121%20234%20127%20174T151%2080L234%20366Q253%20430%20275%20506T308%20618L318%20654Q318%20656%20294%20669L276%20676ZM181%2042Q207%2016%20250%2016Q291%2016%20324%2047Q354%2078%20366%20136T378%20356Q378%20470%20372%20528T349%20616L348%20613Q348%20611%20264%20326L181%2042Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-2260%22%20d%3D%22M166%20-215T159%20-215T147%20-212T141%20-204T139%20-197Q139%20-190%20144%20-183L306%20133H70Q56%20140%2056%20153Q56%20168%2072%20173H327L406%20327H72Q56%20332%2056%20347Q56%20360%2070%20367H426Q597%20702%20602%20707Q605%20716%20618%20716Q625%20716%20630%20712T636%20703T638%20696Q638%20692%20471%20367H707Q722%20359%20722%20347Q722%20336%20708%20328L451%20327L371%20173H708Q722%20163%20722%20153Q722%20140%20707%20133H351Q175%20-210%20170%20-212Q166%20-215%20159%20-215Z%22%3E%3C%2Fpath%3E%0A%3C%2Fdefs%3E%0A%3Cg%20stroke%3D%22currentColor%22%20fill%3D%22currentColor%22%20stroke-width%3D%220%22%20transform%3D%22matrix(1%200%200%20-1%200%200)%22%20aria-hidden%3D%22true%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2200%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(556%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-69%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%221473%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-30%22%20x%3D%221918%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-3C%22%20x%3D%222696%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-69%22%20x%3D%223753%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-3C%22%20x%3D%224376%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-6E%22%20x%3D%225432%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(6033%2C0)%22%3E%0A%3Ctext%20font-family%3D%22monospace%22%20stroke%3D%22none%22%20transform%3D%22scale(71.759)%20matrix(1%200%200%20-1%200%200)%22%3E%EF%BC%8C%3C%2Ftext%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-44%22%20x%3D%226965%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-28%22%20x%3D%227794%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(8183%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-69%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%229100%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(9545%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMAIN-31%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-29%22%20x%3D%2210572%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-3D%22%20x%3D%2211239%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2205%22%20x%3D%2212295%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%2212796%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-44%22%20x%3D%2213241%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-28%22%20x%3D%2214070%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(14459%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMAIN-31%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%2215485%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(15931%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-78%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-69%22%20x%3D%22809%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-29%22%20x%3D%2216847%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2260%22%20x%3D%2217515%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2205%22%20x%3D%2218571%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%3C%2Fsvg%3E#card=math&code=%7B%5Cforall%7Dx_i%2C0%3Ci%3Cn%EF%BC%8C%20D%28x_i%2Cx_1%29%3D%5Cemptyset%2C%20D%28x_1%2Cx_i%29%20%5Cneq%20%5Cemptyset&id=Jmyb3)
由此可知为有向连通图。
定义元素的深度(Path)为到的最短路径长度,即:
对中的每一个环,定义其首节点为最小深度元素为,若存在多个符合条件的元素,取由出发得到的尾节点深度最小的一个。则环的第一条路径为![](https://cdn.nlark.com/yuque/__latex/39eb90461a966647f5e4bd58ca10b6f9.svg#card=math&code=D%28x_i%27%2Cx%7Bx%2B1%7D%27%29&id=UitOX),最后一条路径为。现删除每一个环中最后一条路径,可得为有向连通无环图。根据图论,我们知道这样的图可以转化为一棵树,这棵树就是我们的研究对象,我们称它为集合的依赖树。它的复杂度,也即集合的系统复杂度。
现引入关于单个依赖关系的复杂度(Complex)函数
因为,可得
则由元素引入的复杂度为
则整个集合的复杂度为
由(1.3)单个依赖复杂度的定义可知,其复杂度主要由依赖元素的深度决定。 由(1.4)元素对依赖复杂度定义可知,其复杂度与依赖关系数目,与依赖元素的深度都成正比。 由(1.5)集合的依赖复杂度可知,其复杂度为所有元素排列而成的元素对,其依赖关系带来的复杂度的总和。
这些特征,表现在依赖树的形状上,则为高度越高的节点,其依赖关系带来的复杂度越大。每个节点的分岔越多,该节点的复杂度越大。整棵树的所有节点的复杂度加和,即为集合的复杂度。
对应到软件系统中则表现为,深度的、数量繁多的依赖关系,导致软件系统的模型固化,难以测试和修改。
动态系统的形式化定义
上述我们基于集合的概念和运算,讨论了依赖关系的复杂度计算方式,但是这是一种静态的复杂度。现实中的系统,往往处于自变化或因变化的动态过程中,静态复杂度只是系统某一个时刻的复杂度情况。对此,我们引入新的形式化模型,便于我们展开动态系统的复杂度讨论。
我们定义动态系统的S(System)为一个元组,其中X为系统中的元素集合,D(X)为依赖集合,T(Transformation)表示系统的变化集合,t表示时间分量,即:
在某个时间点,我们可得系统的静态分量:
我们定义系统的时间函数为:
由时间函数,我们可定义系统的时间变化量为:
动态系统的复杂度是无法衡量的,它的时间维度无法被量化,我们只讨论它的静态分量的复杂度,且认为它就是依赖关系的复杂度,即:
认知特征的形式化定义
基于抽象系统的复杂度讨论,我们阐述了复杂度的产生因素和计算方式。通过公式,我们可以了解到,一个具备一定规模的元素和依赖关系的系统,其复杂度都会非常庞大。而人类的短期记忆容量是非常有限的,据科学分析,人类的非连续记忆数量不超过七个。但现实中的系统往往是非常庞大的,尤其是软件系统,那么,为什么我们能够理解和掌握它们呢?关键在于我们有长期的记忆。
我们认知能够将系统中的元素、依赖关系、非抽象部分的行为特征,概括成长期记忆—概念,从而简化系统的依赖模型。通过概念的管理,来认知整体的系统,我们将这种复杂度称为认知复杂度,而上述的集合依赖复杂度称为绝对复杂度。这种通过概念来简化认知系统的行为,可以看做基于静态分量的一个拆分运算:
其中的复杂度是一个常量:
由此,基于(2.5)静态系统的复杂度,我们可以得:
基于认知拆分函数和复杂度简化函数,我们可以推出,当拆分出来的中包含的元素和依赖关系越多,中剩余的就越少,简化后的系统复杂度就越低。若系统经由次拆分之后,得到(3.2)中的,即系统的各个部分都能够被认知系统合理抽象化,我们可以得到一个复杂度为常数的系统,我们称这样的系统为标准化系统:
若系统经由任意次拆分之后,得到,我们称这样的系统为非标准化系统,其中非标准化成程度为:
在维护系统过程中,由系统的非标准化部分引起的,无法使用概念的记忆、关联、推理,而只能依靠短期记忆或者外部记录辅助变更或者扩展,因而造成的注意力负担,我们叫做认知负载。维护系统需要的认知负载越高,则产生缺陷的概率越高,维护的效率越低。
认知能力的形式化我们就介绍到这里,接下来我们探讨一下具体的一些认知简化方式。人类的认知研究本身也构成了一门学科,在此不做详细的探讨,只探讨常见的思维习惯。我们按照复杂性科学中的复杂度空间,将这些简化方式做一个简单的分类。
设有基于静态系统的认知函数
对于以下认知方式:
结构简化
- 过程
对于,在系统中,这些依赖总是按照一定的时间顺序排列,并且完成某一特定功能,我们称为一个过程。
- 行为
对于,在系统中,它总是参与某一些过程,并完成这些过程中相同功能,我们称具有某种行为。
- 结构
对于,不论出现在哪个系统中,或者不论存在系统的哪个部分中,总是能够且完整地存在,我们称为一个结构。
由结构的定义可知,结构属于一种强认知行为,它要求属于结构的这些元素和依赖关系,具备本质性或广泛的普遍性,而不会轻易被系统自身的特征左右。
- 概念
对于,其成员总是拥有一些固定的行为,且总是拥有一些固定的结构,我们称为概念。需要注意的是,既可以是元素,也可以是概念,也就是说概念是可以嵌套的。
概念实际上是对行为和结构的统一抽象,我们在设计系统概念的时候,通常遵循物理世界的逻辑模型,因为这些模型,是每个人都熟悉的,无需验证且稳定的。这样的结构,既能保证系统稳定,而且最大程度降低认知成本。
基于系统组成的简化,我们就介绍到这里,另外,我们设计系统是为了解决问题的,因此,结合我们的目的,我们还有一些简化方式。
功能简化
- 组件
对于,如果总是包含一组固定的概念,且我们总是利用它们去解决一些固定类型的问题,我们称为组件。
- 模式
对于,如果全部或绝大部分由某种组件构成,我们称具有某种模式。
- 子系统
为了减小系统规模,我们可以通过设计,将系统拆分成多个部分,每个部分的元素,不再有设计意图之外的关联方式,并且当我们整体系统时,不再考虑部分内部的逻辑结构,我们称这种部分为子系统。这一部分的形式化语言,也就是(3.1)和(3.2)的认知形式化定义。也就是说,排除规模上的限定,所有的认知行为,本质上都是子系统的抽象。
多人协同的简单探讨
前面我们讨论了动态系统的复杂度和认知的简化效果,但这是单人视角下的情况,还没有考虑多人协同产生的问题。
当有多人参与系统的构建和维护时,每个人对系统的认知都不一样,对系统各部分的设计意图理解也可能不一样,从而在维护工作总,反哺到系统的差异度和非标准化上,循环往复之下,情况就变得更加复杂起来。
我们再次引入动态系统的形式语言进行描述,设在时刻,有一个系统的静态分量:
其中为个人组成的维护团队,其认知函数为:
%22%20aria-hidden%3D%22true%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-46%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-74%22%20x%3D%22910%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-3D%22%20x%3D%221276%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(2333%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-46%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMAIN-31%22%20x%3D%22910%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%223430%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(3875%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-46%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMAIN-32%22%20x%3D%22910%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%224973%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2E%22%20x%3D%225418%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2E%22%20x%3D%225863%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2E%22%20x%3D%226308%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%226753%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(7198%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-46%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-6E%22%20x%3D%22910%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%3C%2Fg%3E%0A%3C%2Fsvg%3E#card=math&code=Ft%20%3D%20F_1%2CF_2%2C…%2CF_n&id=eZEI3)
此时其对于每个人都是标准化的,即:
随后系统经历了一个单位时间的变化,根据(2.4)动态系统的变化量,我们可以推出,对于![](https://cdn.nlark.com/yuque/__latex/46f386f95a8e6daad26b8a18153f2e61.svg#card=math&code=S%7Bt%2B1%7D&id=tPhaO)有:
那么对成员认知复杂度函数为:
单位时间的协同认知复杂度增量表示出它是一个非标准值,对于每一个成员而言,其增量并不相同。并且,其复杂度取决于其他成员对系统的认知简化程度。
如果对于,那么可以推出,即对每个人依然是一个标准系统,我们称之为共识。
相反,如果,那么对每个人都不是标准化系统,且其非标准程度并不一致。在系统演化到版本时,会出现大致两种极端情况:
- 在的演化中,每个人负责维护之前的模块,即系统变化量主要为
- 在的演化中,每个人负责维护不同的模块,即系统变化量主要为
通常,由于每个人基于自己的认知维护系统而产生的变化量,其复杂度增量为常数。我们可以推定
- 情况1下,的复杂度增量,近似于。
- 情况2下,的复杂度增量,大于,且取决于每个%22%20aria-hidden%3D%22true%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-54%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-69%22%20x%3D%22826%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2C%22%20x%3D%22928%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(1373%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-54%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20transform%3D%22scale(0.707)%22%20xlink%3Ahref%3D%22%23E1-MJMATHI-6A%22%20x%3D%22826%22%20y%3D%22-213%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%3C%2Fg%3E%0A%3C%2Fsvg%3E#card=math&code=T_i%2CT_j&id=PUPzr)的共识程度。因为对非共识部分的变更会,会对原有设计产生不可预知的破坏,进而在下一次迭代中,引入更多的非共识成分。
在多数情况下,系统会因为不可抗力因素,无法保证人员和变化量的持久匹配关系,进而推动人员和变量的主动错配(培养备选维护人员)。因此,系统的复杂度递增情况,总是间与两种情况之间,并且随者时间的推移,会朝着难以理解和描述的复杂度情形演进。
基于我们的简单探讨,我们可以了解到系统变化过程中,复杂度增长的大致过程。并且意识到,控制人员和变化量的匹配关系,以及增加所有人员对变化量的共识,是控制动态协同系统复杂度的核心办法。由建立共识产生的成本,我们称作协同成本。
对与软件系统的维护来说,常见的协同方式有技术宣导、代码注释、文档资料、技术规范、以及自动化工具,它们的协同成本和共识效果大致如下:
协同方式 | 协同成本 | 共识效果 |
---|---|---|
技术宣导 | 低 | 弱 |
代码注释 | 低 | 中 |
文档资料 | 高 | 强 |
技术规范 | 中 | 强 |
自动化工具 | 极低 | 极强 |
%E5%8D%8F%E5%90%8C%E6%95%88%E7%94%A8%E8%A1%A8%3C%2Ftitle%3E%0A%3Cdefs%20aria-hidden%3D%22true%22%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-28%22%20d%3D%22M94%20250Q94%20319%20104%20381T127%20488T164%20576T202%20643T244%20695T277%20729T302%20750H315H319Q333%20750%20333%20741Q333%20738%20316%20720T275%20667T226%20581T184%20443T167%20250T184%2058T225%20-81T274%20-167T316%20-220T333%20-241Q333%20-250%20318%20-250H315H302L274%20-226Q180%20-141%20137%20-14T94%20250Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-34%22%20d%3D%22M462%200Q444%203%20333%203Q217%203%20199%200H190V46H221Q241%2046%20248%2046T265%2048T279%2053T286%2061Q287%2063%20287%20115V165H28V211L179%20442Q332%20674%20334%20675Q336%20677%20355%20677H373L379%20671V211H471V165H379V114Q379%2073%20379%2066T385%2054Q393%2047%20442%2046H471V0H462ZM293%20211V545L74%20212L183%20211H293Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-2E%22%20d%3D%22M78%2060Q78%2084%2095%20102T138%20120Q162%20120%20180%20104T199%2061Q199%2036%20182%2018T139%200T96%2017T78%2060Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-31%22%20d%3D%22M213%20578L200%20573Q186%20568%20160%20563T102%20556H83V602H102Q149%20604%20189%20617T245%20641T273%20663Q275%20666%20285%20666Q294%20666%20302%20660V361L303%2061Q310%2054%20315%2052T339%2048T401%2046H427V0H416Q395%203%20257%203Q121%203%20100%200H88V46H114Q136%2046%20152%2046T177%2047T193%2050T201%2052T207%2057T213%2061V578Z%22%3E%3C%2Fpath%3E%0A%3Cpath%20stroke-width%3D%221%22%20id%3D%22E1-MJMAIN-29%22%20d%3D%22M60%20749L64%20750Q69%20750%2074%20750H86L114%20726Q208%20641%20251%20514T294%20250Q294%20182%20284%20119T261%2012T224%20-76T186%20-143T145%20-194T113%20-227T90%20-246Q87%20-249%2086%20-250H74Q66%20-250%2063%20-250T58%20-247T55%20-238Q56%20-237%2066%20-225Q221%20-64%20221%20250T66%20725Q56%20737%2055%20738Q55%20746%2060%20749Z%22%3E%3C%2Fpath%3E%0A%3C%2Fdefs%3E%0A%3Cg%20stroke%3D%22currentColor%22%20fill%3D%22currentColor%22%20stroke-width%3D%220%22%20transform%3D%22matrix(1%200%200%20-1%200%200)%22%20aria-hidden%3D%22true%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-28%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(389%2C0)%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-34%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-2E%22%20x%3D%22500%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-31%22%20x%3D%22779%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMAIN-29%22%20x%3D%221669%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3Cg%20transform%3D%22translate(2058%2C0)%22%3E%0A%3Ctext%20font-family%3D%22monospace%22%20stroke%3D%22none%22%20transform%3D%22scale(71.759)%20matrix(1%200%200%20-1%200%200)%22%3E%E5%8D%8F%3C%2Ftext%3E%0A%3C%2Fg%3E%0A%3Cg%20transform%3D%22translate(2991%2C0)%22%3E%0A%3Ctext%20font-family%3D%22monospace%22%20stroke%3D%22none%22%20transform%3D%22scale(71.759)%20matrix(1%200%200%20-1%200%200)%22%3E%E5%90%8C%3C%2Ftext%3E%0A%3C%2Fg%3E%0A%3Cg%20transform%3D%22translate(3924%2C0)%22%3E%0A%3Ctext%20font-family%3D%22monospace%22%20stroke%3D%22none%22%20transform%3D%22scale(71.759)%20matrix(1%200%200%20-1%200%200)%22%3E%E6%95%88%3C%2Ftext%3E%0A%3C%2Fg%3E%0A%3Cg%20transform%3D%22translate(4857%2C0)%22%3E%0A%3Ctext%20font-family%3D%22monospace%22%20stroke%3D%22none%22%20transform%3D%22scale(71.759)%20matrix(1%200%200%20-1%200%200)%22%3E%E7%94%A8%3C%2Ftext%3E%0A%3C%2Fg%3E%0A%3Cg%20transform%3D%22translate(5789%2C0)%22%3E%0A%3Ctext%20font-family%3D%22monospace%22%20stroke%3D%22none%22%20transform%3D%22scale(71.759)%20matrix(1%200%200%20-1%200%200)%22%3E%E8%A1%A8%3C%2Ftext%3E%0A%3C%2Fg%3E%0A%3C%2Fg%3E%0A%3C%2Fsvg%3E#card=math&code=%284.1%29%E5%8D%8F%E5%90%8C%E6%95%88%E7%94%A8%E8%A1%A8&id=vEO1E)
关于协同最终要的一点是,也是我们最容易走进误区的一点是,评估任何协同方式的成本和效用时,不应该以某个人或者角色的角度出发,而要看对整个团队维护此系统是否产生积极的作用。只有这样,整个团队和系统才能处于协调演进的状态。
软件系统的设计准则
我曾产生过这样的疑问,为什么我们学了很多数据结构、算法、设计模式等等知识,一旦到了真实的编程场景就很少用的上?尽管在局部问题的解决上它们确实重要,尽管学习这些知识确实帮助我们理解程序的本质,培养了编程和设计能力,但不可否认的是,我们在做设计、编码、维护、集成、测试、重构的时候,所面临的问题基本不是这些知识框架所描述的面貌,甚至无从关联,也就无从应用,为什么?
我的答案是,这是计算科学(软件科学)的知识和教育的缺失。如果有一个算法问题,或则一个问题能被抽象成算法问题,我们大概率都能够找到最优解,并将代码优化得非常高效和简洁,因为算法有严谨的理论框架,并且我们受过算法的训练。但是如果是一个系统、模块、方案的设计,我们就没有标准理论系统去参考,也没有受过这样的训练。但是,这样的理论系统是不存在的吗?我们在此就提出一个——系统(模块、方案等)设计的准则是最小标准化。接下来,我们来扩展一下这个准则,按照有效性优先原则,它们有:
1.标准化偏差最小
所有软件创作都包括了本质性工作(essential task)和附属性工作(accidental task)。前者是去创造出一种由抽象的软件实体所组成的复杂概念结构,后者则是用编程语言来表现这些抽象的实体,并在某些空间和速度的限制之下,将程序对应至机器语言。
标准化偏差最小,也叫认知复杂度最小。借用佛瑞德·布鲁克斯在《没有银弹》中对软件工作的阐述,我们得到一个基本认识,即软件系统的创造,主要工作在于它的概念系统的创造。那么标准化偏差最小,指的就是这个概念系统的最小,因此多人协同条件下的认知复杂度也最小。
按照数值的计算方式,我们首先要确定在特定问题下,标准化概念系统是怎样的,再计算当前的概念系统的差异程度。但是显然,这个非标准程度是一个抽象的统计量,很难计算出来。但这不妨碍我们在解决具体问题时,通过寻找和逼近这样的标准概念系统,来解决复杂度问题。所以最关键的是如何找到这样的标准化系统,按照有效程度排列,它可们以分为参照:
a. 技术标准
b. 物理概念
c. 领域模型
d. 行业惯例
e. 自定义标准
2.绝对复杂度最小
我们已经分析过绝对复杂度的定量方式,要使得它最小,按照系统、模块、实现的逻辑层级,分别要求
a. 系统规模最小、耦合度最低
b. 可集成、可测试、可扩展的单元最小
c. 圈复杂度、基本复杂度最小
3.因变化范围最小
所谓因变化,是相对自变化而言的,即当需求、功能、技术方案、实现细节改变时,在系统、模块、实现等各层级上,所牵涉的内部、外部改动流程最短、范围最小。因变化范围最小,实际上在要求各个层级的高内聚性,在系统集成和运行时,暴露尽可能少的信息给外部。
4.多人协同的成本最低
在满足前面条件之后,我们的系统已经有良好的可维护性和可扩展性,此时,才是我们追求维护成本的时机。本质上讲,上述所有的措施都有利于降低系统长期维护成本,反过来,如果为了降低维护成本,而忽略前面的要求,最终是南辕北辙的。具体措施,可以参考。
常见复杂度调控方法及原理
我们已经描绘出了软件系统的形式化框架,并提出了设计准则。现在,我们试着用这样的框架,去统一在软件工程中用于控制复杂度的各种方法,避免总是需要分散记忆或者模糊理解,甚至在部分决策场景下不同方法导致的理论矛盾。
API设计、命名规范
它是一个综合性的复控(复杂度控制)方法。如果我们把API设计和命名,看做是概念创作阶段的工作,它涉及到降低标准度偏差、绝对复杂度和因变化范围。如果我们把它看做是概念实现阶段的工作,那么它旨在提高代码的可读性,降低多人协同成本。
系统分层和模块化
系统分层和模块化本质上都在从业务或技术的角度,将大的系统拆分成多个子系统,只是一个是在水平方向,一个是在垂直方向。拆分之后,子系统不在关心它的目标之外的变化因素,可以定义自身的技术标准、领域模型,降低认知复杂度和因变化范围。并且拆分之后,由于减少了层次和模块间的依赖路径,降低了绝对复杂度。但是这种方式,一定程度上会增加协同成本,因此,是一个综合的,有利有弊的复控方法。
依赖注入
依赖注入或者叫依赖管理,是指通过依赖管理框架来实现软件实体的组合,避免实体关系的深化和固化。显然这种方式,是在降低软件实体的依赖深度和耦合度,使得软件的可集成、可测试、可扩展的单元最小,即降低软件的绝对复杂度。
面向切面编程 AOP
AOP是一种编程思想,它认为软件系统中,普遍分布着一些与业务无关的功能点,不应该由业务方实现,而应该以独立的,业务无感知的方式集成到软件系统中。这样的功能,称作软件的切面,比如日志、统计、鉴权、插件化等等。AOP框架与语言的动态特性紧密相连,比如java的字节码编辑,注解,动态代理,反射等等。
显然AOP编程,将软件系统中的切面功能点从大的集合中抽取出来,降低了业务子集的整体规模和依赖关系。因此,它本质上还是在抽取子系统,通过AOP框架,来实现和集成子系统的功能,降低系统的绝对复杂度。AOP实现方式是统一的、可复用的,一个功能面等效于一个子系统,由此可见,AOP是一种高效的子系统抽取模式。
函数式编程 FP
函数式编程基于lamda演算发展而来,它的核心思想是由函数演算来实现系统的不同功能,避免修改和存储系统状态,从而导致系统的不确定性,不稳定性。
函数式编程有许多特征,其中最核心的有两点。第一,没有”副作用”,即函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。第二,引用透明,即函数计算过程,不依赖于任何全局状态。即如果提供同样的输入,那么函数总是返回同样的结果。
函数式编程,使我们可以从形式上推断程序行为,助于验证正确性、简化算法,甚至有助于找出优化它的方法。
函数式编程是一种纯粹、典型的通过控制因变化范围来降低软件系统绝对复杂度的方法。
领域驱动设计
领域驱动设计的核心思想,是将解决特定问题的大量的最佳实践和标准模式抽象成概念系统,并形成特定的语言或技术工具,从而解决该领域内的交流、开发等方面的问题,提高解决效率。这样的一套概念系统和技术工具,称为一个领域。
显然,领域驱动设计是解决标准化偏差的复控方式。
自动化工程
参考,自动化工程是一种降低协同成本的复控方式。
复杂度哲学
尽管我们提出了逻辑框架来解决复杂度问题,避免将复杂度控制工作,交付于个人经验和记忆,但还有一些重要的观点,是任何框架都无法包含的。它们更像是哲学层面的命题,这也可能是所有创造性活动都具有的挑战和魅力之一。
奥卡姆剃刀原则
如无必要,勿增实体。
奥卡姆剃刀原则的提出是哲学和科学的分端,有着深刻和广泛的应用。它也可以被理解为,在没有办法证明情况下,简单有效的理论就是正确的。设计原则中的迪米特法则,也称最少知道原则,可以认为是奥卡姆剃刀原则的一个具体化。
破窗原理
环境中的不良现象如果被放任存在,会诱使人们仿效,甚至变本加厉。以一幢有少许破窗的建筑为例,如果那些窗不被修理好,可能将会有破坏者破坏更多的窗户。
破窗原理更多是一种心理学和社会学层面的一种规律,它同样适用于由人员来维护的软件系统。软件工程中,有关于微重构的论述,还有些资料提到的,不要使用战术编程,而要采用战略编程,本质上就是在解决破窗问题。
没有银弹
复杂的软件工程问题无法靠简单的方案来解决。
复杂度控制,以及解决复杂度引起的问题,都像是打地鼠游戏。没有一种方法,可以敲掉一两只地鼠就获得最终的胜利。只能从全局到细节做好逐一的把控,与复杂度一起走到系统的最后。
总结
构造低复杂度的软件系统不等于构造简单的软件系统,而是要构造能够容纳系统需求的,容易理解和维护的系统。在设计或编码决策时,低复杂度也不等于简单和方便,而是要遵循设计意图和架构,以系统的整体复杂度为目标,满足系统集成后的简单性、可靠性。
最后,希望本文梳理和定义的复杂度及相关控制方式,能对读者在以后的工作学习中有所助益。
参考
【1】系统复杂性及其度量 https://max.book118.com/html/2019/0317/6032223035002015.shtm系统复杂性及度量.pdf
【2】软件复杂度是什么?https://www.jianshu.com/p/c66db5a6db22
【3】阿里研究员:警惕软件复杂度困局 https://zhuanlan.zhihu.com/p/196430574
【4】美团技术团队, 降低软件复杂性的一般原则和方法 https://zhuanlan.zhihu.com/p/83875541
【5】软件工程教程:软件设计的复杂性 https://www.yiibai.com/software_engineering/software_design_complexity.html
【6】百度百科:协同学 https://baike.baidu.com/item/%E5%8D%8F%E5%90%8C%E5%AD%A6/481834
【7】软件衡量指标 https://en.wikipedia.org/wiki/Software_metric