第一章:关于复杂性的介绍

 

作者简介: 常柱,微信公众号【架构未来】作者,十多年一线互联网研发从业经验;擅长大型业务系统架构和技术团队管理工作;对会员体系、技术中台、业务中台、物联网平台等建设有丰富的经验;前五八同城商业会员技术负责人,宝驾租车技术负责人等,现在58到家负责业务中台技术团队。


 

 

编写计算机软件是人类历史上最纯粹的创造性活动之一。程序员不受物理定律等实际限制的约束;我们可以用在现实世界中不可能存在的行为来创造令人兴奋的虚拟世界。编程不需要像芭蕾舞或篮球那样的身体技能或协调能力。所有的编程需要的是一个创造性的头脑和能组织你的想法的能力。如果你能把一个系统形象化,你也许就能在计算机程序中实现它。

这意味着,在编写软件的过程中,最大的限制是我们理解自己正在创建的系统的能力。随着程序的完善和功能的叠加,系统会变得更加复杂,其组件之间依赖关系会变得极其微妙。随着时间的推移,复杂性不断增加,当程序员修改系统时,他们越来越难以记住所有相关的因素。这会降低开发速度并导致bug,从而进一步降低开发速度并增加开发成本。在任何程序的生命周期中,复杂性都会不可避免地增加。程序越大,参与其中的人员越多,管理复杂性就越困难。

好的开发工具可以帮助我们处理复杂性,在过去的几十年里,已经创建了许多伟大的工具。但是,我们单独使用工具的能力是有限的。如果我们想让编写软件变得更容易,这样我们就可以以更低的成本,构建更强大的系统,我们必须找到使软件更简单的方法。尽管我们尽了最大的努力,复杂性仍然会随着时间的推移而增加,但是更简单的设计允许我们在复杂性变得不可抗拒之前构建更大、更强大的系统。

有两种对付复杂性的一般方法,这两种方法都将在本书中讨论。第一种方法是通过使代码更简单、更明显来消除复杂性。 例如,可以通过消除特殊情况或以一致的方式使用标识符来降低复杂性。

处理复杂性的第二种方法是封装它,这样程序员就可以在一个系统上工作,而不必一次暴露系统的所有复杂性。这种方法称为模块化设计。 在模块化设计中,软件系统被分成模块,如面向对象语言中的类。这些模块被设计成相对独立的,这样程序员就可以在一个模块上工作,而不必了解其他模块的细节。

由于软件的可塑性很强,软件设计是一个持续的过程,它跨越了软件系统的整个生命周期;这使得软件设计不同于建筑物、船只或桥梁等物理系统的设计。然而,人们并不总是这样看待软件设计。在编程的大部分历史中,设计集中在项目的开始阶段,就像在其他工程学科中一样。这种方法的最极端被称为瀑布模型,在该模型中,项目被划分为离散的阶段,如需求定义、设计、编码、测试和维护。在瀑布模型中,每个阶段在下一个阶段开始之前完成;在许多情况下,每个阶段由不同的人负责。在设计阶段,整个系统同时被设计。设计在此阶段的末尾被冻结,随后阶段的作用是充实和实现该设计。

不幸的是,瀑布模型很少适用于软件。软件系统本质上比物理系统更复杂;在构建任何东西之前,不可能很好地可视化大型软件系统的设计以理解它的所有含义。因此,最初的设计将会有很多问题。在实施顺利进行之前,问题不会变得明显。然而,瀑布模型的结构并不能适应此时的主要设计更改(例如,设计人员可能已经转移到其他项目)。因此,开发人员试图在不改变整体设计的情况下修补问题。这导致了复杂性的爆炸。

由于这些问题,现在大多数软件开发项目都使用增量的方法,比如敏捷开发,在这种方法中,最初的设计集中在整体功能的一个小子集上。设计、实现并评估这个子集。发现并纠正原始设计中的问题,然后再设计、实现和评估其他一些特性。每次迭代都暴露了现有设计的问题,这些问题在设计下一组特性之前得到了修复。通过以这种方式展开设计,可以在系统仍然很小的时候解决初始设计的问题;后期的特性受益于在早期特性实现过程中获得的经验,因此它们的问题更少。

增量方法适用于软件,因为软件具有足够的延展性,允许在实现过程中进行重大的设计更改。相比之下,对于物理系统来说,主要的设计变化更具挑战性:例如,在建造过程中改变支撑一座桥的塔的数量是不现实的。

增量开发意味着软件设计永远不会完成。设计在系统的生命周期中不断发生:开发人员应该始终考虑设计问题。增量式开发还意味着持续的重新设计。 一个系统或组件的最初设计几乎从来都不是最好的;经验不可避免地显示出更好的做事方法。作为一名软件开发人员,您应该始终寻找机会来改进您正在处理的系统的设计,并且您应该计划将一部分时间花在设计改进上。

如果软件开发人员应该始终考虑设计问题,并且减少复杂性是软件设计中最重要的元素,那么软件开发人员应该始终考虑复杂性。 这本书是关于如何使用复杂性来指导整个软件生命周期的设计。

这本书有两个总体目标。首先是描述软件复杂性的本质:“复杂性”是什么意思,为什么它很重要,以及如何识别一个程序何时具有不必要的复杂性?本书的第二个,也是更具挑战性的目标是介绍你可以在软件开发过程中使用的技术,以最小化复杂性。不幸的是,没有一个简单的方法可以保证优秀的软件设计。相反,我将展示一组与哲学相关的高级概念,比如“类应该是深度的”或“定义不存在的错误”。这些概念可能不能立即确定最佳的设计,但是您可以使用它们来比较设计方案并指导您对设计空间的探索。

1.1 如何使用这本书

这里描述的许多设计原则有些抽象,因此如果不查看实际代码,可能很难理解它们。要找到足够小到可以包含在书中,但又足够大到可以用实际系统说明问题的示例是一个挑战(如果您遇到好的示例,请将它们发送给我)。因此,这本书本身可能不足以让你学会如何应用这些原则。

使用这本书的最佳方法是结合代码评审。 当您阅读其他人的代码时,请考虑它是否符合这里讨论的概念,以及它如何与代码的复杂性相关。别人的代码比你自己的代码更容易发现设计问题。您可以使用这里描述的危险信号来识别问题并提出改进建议。审查代码还将使您了解新的设计方法和编程技术。

提高您的设计技能的最佳方法之一是学会识别危险信号:一段代码可能比它需要的更复杂的信号。 在这本书的过程中,我将指出与每个主要设计问题相关的问题的危险信号;最重要的在书的后面有总结。然后您可以在编码时使用它们:当您看到一个危险信号时,停止并寻找一个可以消除问题的替代设计。 当你第一次尝试这种方法时,您可能必须尝试几个设计备选方案,然后才能找到消除危险信号的方案。不要轻易放弃:你在解决问题之前尝试的选择越多,你学到的就越多。随着时间的推移,你会发现你的代码越来越少的危险信号,你的设计也越来越干净。您的经验还将向您展示其他可以用来识别设计问题的危险信号(我很高兴听到这些)。

在应用本书的观点时,重要的是要适度和谨慎。任何规则都有例外,任何原则都有限度。如果你把任何设计理念发挥到极致,你可能会陷入一个糟糕的境地。漂亮的设计反映了相互竞争的思想和方法之间的平衡。有几个章节的标题是“做得太过了”,描述了当你做了一件好事时如何识别它。

这本书中几乎所有的例子都是用Java或C++编写的,大部分讨论都是关于用面向对象的语言设计类。然而,这些想法也适用于其他领域。几乎所有与方法相关的思想都可以应用于没有面向对象特性的语言中的函数,如c。设计思想也适用于类以外的模块,如子系统或网络服务。

在此背景下,让我们更详细地讨论导致复杂性的原因,以及如何简化软件系统。


 

免责声明:本翻译内容仅供学习使用,版权归英文原作者或出版方所有,若有侵权,请联系删除。

 

 

 

posted @ 2019-12-16 14:47  peida  阅读(1778)  评论(0编辑  收藏  举报