你们修筑、修筑,预备道路,将绊脚石从我百姓的路中除掉。                                        —《以赛亚书》第57章14节

我们要看的第一个软件系统名为“混乱大都市”。它是我喜欢回顾的一个系统—既不是因为它很好,也不是因为它让参与开发的人感到舒服,而是因为当我第一次参与它的
开发时,它教给了我有价值的软件开发经验。

我第一次接触“混乱大都市”,是在我加入了创建它的公司时。初看上去这是一份有前途的工作。我将加入一个团队,参与基于Linux的、“现代”的C++代码集开发,已有的
代码集已经开发几年了。如果你像我一样拥有特殊的技术崇拜,就会觉得很兴奋。

工作起初并不顺利,但是你不能指望在加入一个新团队、面对新的代码集时会觉得很轻松。然而,日复一日(周复一周),情况却没有任何好转。这些代码要花极长的时间来学习,没有显而易见的进入系统中的路径。这是个警告信号。从微观的层面来说,也就是从每行程序、每个方法、每个组件来看,代码都是混乱而粗糙地垒在一起的。不存在一致性、不存在风格、也没有统一的概念能够将不同的部分组织在一起。这是另一个警告信号。系统中的控制流让人觉得不舒服,无法预测。这又是一个警告信号。系统中有太多的“坏味道”(Fowler 1999),整个代码集散发着腐烂的气味,是在大热天里散发着刺激性气体的一个垃圾堆。这是一个清晰的警告信号。数据很少放在使用它的地方。经常引入额外的巴罗克式缓存层,目的是试图让数据停留在更方便的地方。这又是一个警告信号。

当我试图在大脑中建立“大都市”的全图时,没有人能解释它的结构;没有人知道它的所有层、它的藤蔓,以及那些黑暗、隔离的角落。实际上,没有人知道它究竟有多少部分是真正能工作的(它实际上靠的是运气和英雄式的维护程序员)。人们知道他们面对的那一小部分区域,但没人了解整个系统。很自然,没有任何文档。这也是一个警告信
号。我需要的是一份地图。

这是一个悲伤的故事,我曾是其中的一部分:“大都市”是城市规划的恶梦。在你开始整治混乱之前,先要理解混乱,所以我们花了很大的精力和毅力,得到了一份“架构图”。我们标出了每一条公路、每一条主干道、每一条很少人了解的小路、所有灯光昏暗的辅路,并将它们画在一张主图上。我们第一次看到了这个软件的样子,并不令人赏心悦目。它是一些混乱的区块和线条。为了让它更好理解一些,我们用颜色标出了控制路径,突出了它们的类型。然后我们后退一步看着它。

它令人吃惊。它令人目眩神迷。它就像一只喝醉了的蜘蛛,跌进了一些海报颜料罐里,然后在一张纸上织成了一张彩色的网。它看起来就像图2-1那样(这是一个简化后的版本,细节已经修改了,为了保护那些有罪的人)。事情变得很清楚了。我们画出了伦敦地铁图。它甚至有环线。

图2-1:“混乱大都市”的“架构”

这就是那种让跑遍各地的销售员恼怒的系统。实际上,它与伦敦地铁的相似性让人印象深刻:从系统的一端到另一端有很多条路线,哪条路最好通常是不明显的。地理位置很近的目的地常常很难到达,你希望能在两点之间再挖掘一条隧道。有时候,走出地铁换乘巴士实际上是更好的选择。或者干脆步行。

无论从哪个角度来看,这都不是一个“好的”架构。“大都市”的问题超出了设计的范畴,它涉及开发过程和企业文化。这些问题实际上导致了许多架构腐烂。代码经过几年
的“有机”生长,没有人进行过任何架构设计,而且各个部分是随着时间推移,没有经过太多思考就栓在一起的。我们这么说真的算是客气的了。没有人停下来为代码建立一个理智的结构。它增长、膨胀,成为绝对没有任何架构设计的系统的一个典型。代码集从来不会没有架构。这个系统只是拥有一个很糟糕的架构。

如果我们回顾创建“大都市”的公司的历史,它所处的状态是可以理解的(但是不可宽恕):这是一个初创的公司,快速提供许多新版本的压力很大。延期是不可容忍的—这会带来财务灾难。软件工程师被迫尽其极限,快速交付。所以代码是以一系列疯狂冲刺的方式垒在一起的。


注意:不好的公司结构和不健康的开发过程将在糟糕的软件架构中得到反映。


2.1.1 后果

大都市”缺少城市规划,这带来了许多后果,我们将在这里进行分析。这些后果的影响是很严重的,远远超出了你对不良设计的天真想象。地铁变成了云宵飞车,飞速地朝下猛冲。

不可理解
               正如你已经看到的,“大都市”的架构以及缺乏强制的结构,导致了一个很难理解的软件系统,实际上几乎不可能修改。新加入项目的团队成员(譬如我)会被复杂性惊呆,不能够搞清楚状况。

坏的设计确实会招致在它上面叠加坏的设计(实际上它简直就是迫使你那样做),因为没有一种明智的方式可以扩展该设计。在所有能解决手上工作的方法之中,阻力最小的总会被采用,没有明显的办法来修复这些结构问题,所以只要能减少麻烦,就会扔进去新的功能。


注意:重要的是要保持软件设计的品质。坏的架构设计会招致更坏的架构设计。


               缺乏内聚

系统的组件完全没有内聚性。每个组件本来都应该有一个定义良好的角色,但是它们却包含了一堆杂乱的、不一定相关的功能。这使我们很难确定组件存在的原因,也很难弄明白系统中已经实现了哪项具体的功能。

很自然,这让缺陷修复成为了一场噩梦,严重地影响了软件的品质和可靠性。

功能和数据都放在了系统中错误的地方。许多你认为是“核心服务”的部分却没有在系统的核心部分实现,而是由边远的模块来模拟实现(非常痛苦并且代价很大)。

进一步的软件历史考察揭示了原因:原来的团队中存在个人斗争,所以一些关键程序员开始创建他们自己的软件小帝国。他们把自己认为酷的功能放到他们的模块中,即使它不应该属于那里。为了做到这一点,他们于是又实现了更为巴罗克式的通信机制,把控制连回到正确的地方。


注意:开发团队中健康的工作关系将直接有益于软件设计。不健康的关系和个性膨胀会导致不健康的软件。


内聚和耦合
软件设计的关键品质是内聚和耦合。这不是什么新奇的“面向对象”概
念;自从20世纪70年代出现结构化设计开始,开发者对这一概念已经谈论
了许多年。我们的目标是通过设计使系统的组件具备下列品质:
• 高内聚(Strong cohesion)
内聚是一个测量指标,说明相关的功能如何聚集在一起,模块内的各部分
作为一个整体工作得如何。内聚性是将模块粘成一个整体的胶水。
弱内聚的模块是不良分解的信号。每个模块都必须具有清晰定义的角色,
而不只是一堆不相关的功能。
• 低耦合(Low coupling)
耦合是模块之间独立性的测量指标—它们之间进出“电线”的数量。在
最简单的设计中,模块几乎没有什么耦合,所以彼此间的依赖关系较少。
显然,模块不能够完全解耦,否则它们将根本不能够一起工作!
模块之间的联系有多种方式,有的是直接的,有的是间接的。模块可以调
用其他模块中的函数,或被其他模块所调用。它可能使用其他模块提供的
Web服务或设施,可能使用其他模块的数据类型,或提供某些数据让其他模
块使用(可能是变量或文件)。
好的软件设计会限制通信的线路,只提供那些绝对需要的。这种通信线路
是确定架构的一部分因素。

不必要的耦合
                “大都市”没有清晰的分层。模块之间的依赖关系不是单向的,耦合常常是双向的。组件A会到达组件B的内部,目的是完成它的一项任务。在其他的地方,组件B又通过硬编码调用了组件A。系统没有最底层,也没有控制中心。它是整体式的一大块软件。这意味着系统的各部分之间耦合非常紧密,你想启动系统骨架就不得不创建所有的组件。单个组件的任何改变都会波及其他组件,需要修改许多依赖它的组件。孤立地看代码组件没有任何意义。

这使得低层次的测试不能够进行。不仅是代码层次的测试不可能进行,而且组件层次的集成测试也不能够创建,因为每个组件都依赖于几乎所有其他组件。当然,在公司中,测试从来也不具有很高的优先级(我们根本没有时间来做这种测试),所以这“不成为
问题”。不必说,这个软件不太可靠。


注意:好的设计考虑到内部组件连接的连接机制和连接数(以及连接性质)。系统的单个部分应该能够独立存在。紧耦合将导致不可测试的代码。


代码问题
              不良的顶层设计所带来的问题也影响到了代码层面。问题会引起其他问题(参见Hunt和Davis[1999]中关于破窗理论的讨论)。因为没有通用的设计,也没有整体项目“风格”,所以也没有人关心共同的编码标准、使用共同的库,或采用共同的惯例。组件、类和文件都没有命名惯例。甚至都没有共同的构建系统。胶带、Shell脚本、Perl胶水与makefile和Visual Studio项目文件混在一起。编译这个怪物被视为一场复杂的成人仪式!

“大都市”最微妙而又最严重的问题是重复。由于没有清晰的设计,也不清楚功能应该处于的位置,所以轮子在整个代码集中不断重新发明。一些简单的东西,如通用算法和数据结构,在许多模块中重复出现,每种实现都带有自己的一些未知的缺陷和怪异的行
为特征。更大范围的关注点,如外部通信和数据缓存,也实现了许多次。

更多的软件历史考察揭示了原因:“大都市”开始是从一系列独立的原型拼起来的,这些原型本该抛弃。“大都市”实际上是偶然形成的城市群。当代码组件缝合在一起时,组件之间匹配得不好。随着时间的推移,这种随意的缝合开始破裂,所以组件互相拉扯,
导致代码集破碎,而不是和谐地协作。


注意:松弛而模糊的架构将导致每个代码组件编写得不好,并且相互之间匹配得不好。它也会导致重复的代码和工作。


代码以外的问题“大都市”内部的问题已经超越了代码集,在公司中其他的地方导致了混乱。不仅开发团队中有问题,而且架构的腐烂也影响到了支持和使用该产品的人。

开发团队
                   项目的新成员(例如我)被复杂性惊呆了,不能够搞清楚状况。这很好地解释了为什么很少有新人能在公司里待下来—员工流失率非常高。那些留下来的人非常努力地工作,项目的压力非常大。规划新的功能会导致极大的恐惧。

缓慢的开发周期
                    由于维护“大都市”是一项恐怖的任务,所以即使是最简单的变更或“很小的”缺陷修复都不知道要花多少时间。管理软件开发周期非常难。客户只好等着实现重要的特征,管理层对开发团队不能满足业务目标感到越来越沮丧。

支持工程师
                   在支持这个极不寻常的产品时,产品支持工程师度过了可怕的时光,他们要设法弄明白很小的软件版本差异之间错综复杂的行为差异。

第三方支持
                   项目开发了一个外部支持协议,支持其他设备远程控制“大都市”。由于它只是软件内部结构上面薄薄的一层,所以它反映了“大都市”的架构,这意味着它也是巴罗克式的、难以理解的、容易偶尔出错的、不可能使用的。第三方工程师的生活也
被“大都市”的可怕结构搞得一团糟。

公司内部政治
                   开发问题导致了公司内部不同“种族”的分裂。开发团队与营销销售团队之间关系紧张,每次新版本要推出时,制造部门总是要承受巨大的压力。经理们已经绝望了。


注意:不良架构的影响不仅限于代码。它会进一步影响到人、团队、过程和时间表。


                  清晰的需求

软件历史考察凸显了“混乱大都市”之所以混乱的一个重要原因:在项目开始之初,团队并不知道要构建的是什么。本来的初创公司知道它要占领哪个市场,但不知道哪种产品能占领这个市场。所以他们两面下注,要求一个可以做许多事情的软件平台。噢,我们昨天就想得到它了。所以程序员们急急忙忙创建了一个毫无希望的总体基础设施,它具有做任何事情的潜力(但做得不好),而不是创建一个把一件事情做好的架构,并能够在将来进行扩展,做更多的事情。


注意:重要的是要在开始设计系统之前知道你打算设计什么。如果你不知道它是什么,也不知道它将做什么,暂时不要开始设计它。只设计你知道需要的东西。


在规划“大都市”的早期阶段,有太多的架构师。面对糊涂的需求,他们都拿着一块拼不起来的拼图,试图独自工作。他们在工作时没有考虑到整个项目,所以当他们试图将这些拼图拼在一起时,就拼不起来了。没有时间进一步思考架构,软件设计的各个部分有一些重叠,于是开始了“大都市”的城市规划灾难。

2.1.2 现状

“大都市”的设计几乎完全是无可救药的—相信我,随着时间的推移,我们也尝试过修复它。修复工作需要返工、重构、修改代码结构中的问题,这些已经成为不可能的任务。重写也不是省事的方案,因为支持老的、巴罗克式的控制协议是需求的一部分。你可以看到,“大都市”的“设计”产生的后果是残酷的,并且会无情地变得更糟。很难加入新的特性,所以人们只是忙着添加更多不完善的功能、救急补丁和编造的谎言。没有人在面对代码时感到愉快,项目正盘旋着向下栽。缺乏设计导致了不良的代码,从而又导致了不良的团队精神和不断变长的开发周期。这最终导致了公司严重的财务问题。最后,管理层宣布“混乱大都市”已经不盈利了,它被抛弃了。对于任何组织机构来说,这都是勇敢的一步,特别是这个公司一直都眼高手低,同时又试图避免沉沦。带着团队从以前版本中得到的所有C++和Linux经验,他们在Windows上用C#重写了系统。猜猜看会怎么样。
              2.1.3 来自“大都市”的名信片

那么我们学到了什么?不良的架构会产生深远的影响和严重的反弹。在“混乱大都市”中缺少预见性和架构设计,导致了下面的问题:
• 低品质的软件和漫长的版本发布周期。
• 系统没有弹性,不能够适应变更或添加新的功能。
• 无处不在的代码问题。
• 员工问题(压力大、士气低、跳槽等)。
• 大量混乱的公司内部政治。
• 公司不能成功。
• 许多痛苦和面对代码深夜加班。

架构师必看-架构之美第14章-两个系统的故事:混乱大都市(一)相关推荐

  1. Java架构师必看的10本书

    转载自 Java架构师必看的10本书 1.大型网站系统与JAVA中间件实践 本书围绕大型网站和支撑大型网站架构的Java中间件的实践展开介绍. 从分布式系统的知识切入,让读者对分布式系统有基本的了解: ...

  2. sqlrelay mysql_数据库连接池SQL Relay安装使用-Java架构师必看

    SQLRelay按照其官网http://sqlrelay.sourceforge.net/indePHP SQL Relay按照其官网http://sqlrelay.sourceforge.net/i ...

  3. php自动加载指定类,PHP自动加载类-Java架构师必看

    test.php 当前目录下有j.phpPHP test.php function __autoload($class_name) { require_once $class_name . '.php ...

  4. java定义dll文件位置,生成网站时如何设置固定的dll文件名?-Java架构师必看

    在用VS2005发布网站项目时,默认生成bin目录下的.dll文件名是随机命名的; 如果要固定生成文网络 在用VS2005发布网站项目时,默认生成bin目录下的.dll文件名是随机命名的; 如果要固定 ...

  5. java url 处理,URL处理-Java架构师必看

    完整URL地址:http://username:password@hostname/path?ar网络 协议:http:// 用户名和密码: username:password  以:将两者分隔 主机 ...

  6. java处理图像库函数_图象处理详解-Java架构师必看

    作者:未知    请与本人联系 在使用ASP的时候,我们时常要借助第三方控件来实现一些图象功能.而数据库 作者:未知     请与本人联系 在使用ASP的时候,我们时常要借助第三方控件来实现一些图象功 ...

  7. 进阶Java架构师必看书:大型架构+框架+性能优化+中间件+分布式

    进阶Java架构师必看书:大型架构+框架+性能优化+中间件+分布式 优知学院 2018-09-05 07:21:00 1.大型网站技术架构:核心原理与案例分析 本书通过梳理大型网站技术发展历程,剖析大 ...

  8. freetextbox java_FreeTextBox-Java架构师必看

    作者:朱俊代码:C#关键字:FreeTextBox.流媒体出自:http://blog.csdn.nJavaScript 作者:朱俊 代码:C# 关键字:FreeTextBox.流媒体 摘要:最近一个 ...

  9. 架构师的工作都干些什么?!想做架构师必看

    转载自  架构师的工作都干些什么?!想做架构师必看 之前有网友说想看架构师升级的文章,所以写了本文.先给本文中架构师做个定义:第一,能力上达到(似乎是废话),第二,公司肯承认,不仅能给架构师的头衔,更 ...

最新文章

  1. golang版try..catch..
  2. Exception: This is not supported, use MenuItemCompat.getActionProvider()的处理
  3. 【笔记】基于轻量和积网络及无人机遥感图像的大豆田杂草识别
  4. 鞭策你一辈子的好文章
  5. 按键改变元素背景颜色 链式编程的原理 评分案例 each方法的使用
  6. endnote x9中科大版_文献管理软件Endnote的一些使用经验
  7. 设计类网站|超实用的工具型网站!
  8. C++_类和对象_C++多态_多态案例1-计算器类_抽象父类---C++语言工作笔记071
  9. python读取、写入txt文本内容
  10. xml python et_python xml处理
  11. 裁员的沃森健康,问题重重的IBM人工智能业务
  12. “九章”量子计算机与智能优化算法
  13. 布谷鸟沙盒分析静态文件_布谷鸟cuckoo
  14. 论文解读:PV-RCNN: Point-Voxel Feature Set Abstraction for 3D Object Detection
  15. 十四、中国为什么没有自己的指令集
  16. perf工具简介+火焰图制作与解读
  17. java 问号_java问号乱码解决方法
  18. 不忘初心,成于自渡——记录2020年的自己
  19. Starting sshd: Missing privilege separation directory: /var/empty/sshd
  20. 计算机网络的速率怎么计算,宽带速率对照表和计算方法

热门文章

  1. java-php-python-ssm基于动漫论坛系统计算机毕业设计
  2. Visual assistx(西红柿)插件的安装及简单使用
  3. 【luogu P3674】 小清新人渣的本愿
  4. IOS常用第三方库《转》
  5. Intel新CEO敲定,斯旺终”转正“ 1
  6. Flash游戏的衰败原因
  7. 小程序 Base64转换字符串
  8. 升级到Win10后悔了?来,教你如何恢复系统
  9. H5网页语音实时播报功能的实现
  10. 大一新生的pta错题归纳