第一章 软件复杂度剖析

计算机编程的本质就是控制复杂度

什么是复杂系统

定义:由大量相互作用的部分组成的系统。于整个系统比起来,这些组成部分相对简单,没有中央控制,组成部分之间也没有全局性的通信,并且组成部分的相互作用导致了复杂行为

复杂系统的组成部分

“组成部分"对于软件系统,就是所谓的"软件元素”,是基于粒度的不同可以是函数、类、模块、组件和服务等。这些软件元素相对简单,然而彼此之间的相互作用却导致了软件系统的复杂行为

复杂度的成因剖析

Jurgen Appelo从理解能力与预测能力两个维度分析了复杂度的成因

  • 理解能力维度——简单的(simple)和复杂的(complicated)
  • 预测能力维度——有序的(ordered)、复杂的(complicated)和混沌的(chaotic)

在这里插入图片描述

两个维度都蕴含"复杂"的含义:前者与简单相对,意为复杂至难以理解【复杂难解】。后者于有序相对,意为发展规律难以预测【复杂难测】。
预测能力维度,"难测"还不是最复杂层次,最高层次为混沌,既不可预测。

两个维度交叉,就此形成了6中不同复杂意义的层次定义:

该书中没有明确定义每个层级的具体名称,只是按照特征进行了分类

  • 有序——简单【内衣】
  • 有序——复杂【手表】
  • 复杂难测——简单【团队】
  • 复杂难测——难以理解【城市】
  • 混沌——简单【双摆】
  • 混沌——复杂【股市】
软件系统属于哪一个层次

大多数软件系统需要实现的整体功能往往是难以理解的,同时,随着需求的不断演进,又具备了未来的不可预测性,这意味着软件系统"复杂"的同时覆盖了"复杂难解"于"复杂难测"两个层面。因此软件系统被定义到城市的复杂特征中。即考虑城市布局,便于居民的生活于工作,满足外来人员的旅游或出差等,同时考虑未来因素的变化,例如"居民的城市的使用方法发送变化,或受到外力的影响时城市进行相应的演化等"。

参考城市的复杂度特征,去剖析软件系统的复杂度,就可以从理解能力预测能力这两个维度探索软件复杂度的成因。

理解能力

影响理解能力的第一要素是规模,关键要素是结构

  • 规模: 软性系统的规模取决于需求的数量,同时也取决于需求数量和需求功能点之间的关系

    由于每个功能彼此之间存在相互影响和相互依赖,这是不可避免的,导致牵一发而动全身,这就会造成软件开发的拥堵现象。
    造成软件开发的拥堵现象的主要原因

    • 函数存在副作用。调用时可能对函数的结果做了隐含的假设;
    • 类的职责过多,导致开发人员不敢轻易修改,开发人员无法确定会影响到哪些模块;
    • 热点代码被频繁变更,职责被包裹了一层又一层,没有清晰的边界;
    • 在系统某个角落,隐藏着伺机而动的bug,当诱发条件具备时,就会让整条调用链路瘫痪;
    • 不同的业务场景包含了不同的例外场景,每种例外场景的处理方式都各不相同;
    • 同步处理代码与异步处理代码纠缠在一起,不可预知程序执行的顺序;

    系统规模的不断扩展,软件复杂度也会增长。这种增长呈现出陡峭的指数级趋势,源自于我们在构建软件时无法避免技术债。在物理学术语中称为"熵"的增长,程序员会说"软件在腐烂"。
    避免技术债的方案

    • 通过技术债列表或者技术债雷达等可视化形式及时呈现给团队,并制定计划主动地消除或降低技术债。尽量让技术债保证它可见;
    • 设计上做到功能之间的正交,降低维护的成本;
    • 为业务逻辑写单元测试,建立功能代码的测试网;

    软件规模的一个显著特征时代码行数。然而,代码行数常常具有欺骗性。如果需求的功能数量与代码行数之间呈现出不成比例的关系,说明该系统的生命体特征可能出现了异常。例如,代码行数的庞大其实可能是一种肥胖症,意为着出现了大量的重复代码。

    在面向对象设计的软件项目里,除了代码行数,包、类、方法的数量,继承的层次以及方法的调用数,还有我们常常提及的圈复杂度,都会或多或少地影响整个软件系统的规模。

  • 结构:结构是决定系统复杂度的一个关键因素

    • 结构之所以变得复杂,多数情况下还是由系统的质量属性决定的;

      结构之所以变得复杂,多数情况下还是由系统的质量属性决定的。
      例:满足高性能、高并发的需求,就需要考虑在系统中引入缓存、并行处理、CDN、异步消息以及支持分区的可伸缩架构。
      例:支持海量数据的高校分析,就要考虑海量数据的分布式存储,如何利用节点的内存与CPU资源执行运算。

    • 软件系统的结构繁复也会增加软件组织的复杂度;

      例如:消息通信不可靠,数据不一致等因为分布式通信导致的意外场景。导致无法改变分布式系统固有的复杂度。

      软件架构的分解促成了软件构建工作的分工,这种分工虽然使得高效的并行开发成为可能,却也可能因为沟通成本的增加为管理带来挑战。
      康威定律就指出:任何组织在设计一套系统(广义概念上的系统)时,所交付的设计方案在结构上都与组织的沟通结构保持一致。

      Sam Newman认为是需要“适应沟通途径”使得康威定律在软件结构与组织结构中生效W1“。他分析了一种典型的分处异地的分布式团队。整个团队共享单个服务的代码所有权,由于分布式团队的地域和时区界限使得沟通成本变高,因此团队之间只能进行粗粒度的沟通。
      当协调变化的成本增加后,人们就会想方设法降低协调和沟通的成本。直截了当的做法就是分解代码,分配代码所有权,物理分隔的团队各自负责一部分代码库,从而能够更容易地修改代码,团队之间会有更多关于如何集成两部分代码的粗粒度的沟通。
      最终,与这种沟通路径匹配形成的粗粒度应用程序编程接口(applicationprogramming interface,API)构成了代码库中两部分之间的边界。
      需要注意的是,与设计方案相匹配的团队结构指的是负责开发的团队组织,而非使用软件产品的客户团队。

      无论设计是优雅还是拙劣,系统结构都可能因为某种设计权衡而变得复杂。唯一的区别在于前者是主动 控制结构的复杂度,而后者带来的复杂度是偶发的,是错误的滋生,是一种技术债,它会随着系统规模的增大产生一种无序设计。

      《架构之美》中第2章“两个系统的故事:现代软件神话”详细地罗列了无序设计系统的几种警告信号:

      • 代码没有显而易见的进入系统中的路径;
      • 不存在一致性,不存在风格,也没有能够将不同的部分组织在一起的统一概念;
      • 系统中的控制流让人觉得不舒服,无法预测;
      • 系统中有太多的“坏味道”;
      • 数据很少放在它被使用的地方,经常引入额外的巴洛克式缓存层,试图让数据停留在更方便的地方;

      一旦逻辑层没有守住自己的边界,分层架构模式就失去了规划清晰结构的价值。随着需求的增加,系统结构会变得越来越混乱,最终陷入无序设计的泥沼。

预测能力

影响预测能力的关键要素在于变化。对变化的应对不妥,就会导致过度设计或者设计不足

  • 过度设计:设计软件系统时,变化让我们无法如何把握系统设计的度。

    • 拒绝对变化做出理智的预测系统的设计会变得僵化,一旦有新的变化发生,修改的成本会非常大;
    • 过于看重变化产生的影响,渴望涵盖一切变化的可能,若预期的变化没有发生,我们之前为变化付出的成本就再也补偿不回来了。

    这就是所谓的“过度设计”

    具有实证主义态度的设计理念是面对不可预测的变化时,应首先保证方案的简单性。当变化真正发生时,可以通过诸如提炼接口(extract interface)的重构手法,满足解析逻辑的扩展。

  • 设计不足:要应对需求变化,终归需要一些设计技巧。

    很多时候,因为设计人员的技能不足,没有明确识别出未来确认会发生的变化,或者对需求变化发展的方向缺乏前瞻所以导致整个设计变得过于僵化,修改的成本太高,从而走向了过度设计的另外一个极端,这一问题称为"设计不足"。

    设计不足的方案只顾眼前,对于一定要发生的变化视而不见,这不仅导致方案缺乏可扩展性,甚至有可能出现技术实现方向的错误。这样的设计不是恰如其分的简单设计,而是对于糟糕质量视而不见的简陋处置,是为了应付进度蒙混过关用的临时花招,表面看来满足了进度要求,但在未来偿还欠下的债务时,需要付出几倍的成本。如果整个软件系统都由这样设计不足的方案构成,那么未来任何一次需求的变更或增加,都可能成为压垮系统的最后一根稻草。

    我们无法预知未来,自然就无法预测未来可能发生的变化,这就带来了软件系统的不可预测性。软件设计者不可能对变化听之任之,却又因为它的不可预测性而无可适从。在软件系统不断演化的过程中,面对变化我们需要尽可能地保证方案的平衡:既要避免因为设计不足使得变化对系统产生根本影响,又要防止因为满足可扩展性让方案变得格外复杂,最后背上过度设计的坏名声。故而,变化之难,难在如何在设计不足与过度设计之间取得平衡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抹灰丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值