【可重用性】重复使用的失信承诺
视频号
微信公众号
知识星球
重用是IT的圣杯:每当一个新的架构范式来到IT小镇时,“重用”就是为什么要采用该范式的核心论点之一。商业销售宣传通常是这样的:“切换到将花费一些前期资金,但由于它能够重复使用,它很快就会得到回报”。
我不知道你的经历,但我从未真正看到它以承诺的方式发挥作用。无论你看面向对象、CORBA、基于组件的体系结构、EJB、SOA,你都能想到:重用的实现程度比承诺的要小得多,这意味着承诺的业务案例从未实现。此外,往往在一段时间后出现了非常脆弱和紧密耦合的系统,这些系统很难更改和操作,即业务案例开始变成相反的情况。但幸运的是,在这个时候,通常下一个圣杯范式出现了,周期又开始了。
简而言之:基于任何架构范式的重用从来没有像承诺的那样奏效,承诺总是被打破。
今天呢?目前,微服务是当今的典范,当然,重复使用是伴随它而来的商业案例承诺。这次行吗?我们终于找到了承诺已久的银弹了吗?
我不知道你对此的看法,但我相信,如果我们在微服务方法中推动重用,我们将再次面临一个完全混乱的架构,包括一个非常糟糕的商业案例。
这就引出了一个问题:为什么重复使用的承诺被打破了?
重用承诺被打破的原因有几个。以下是我可能不完整的观察结果:
首先,良好的可重用性是昂贵的。Fred Brooks多年前为可重用组件创造了“9规则”(这里的“组件”仅指重用的基本构建块,没有附加额外的语义)。根据他的观察,与仅为简单使用而设计的组件相比,设计一个可以正确重复使用的组件所花费的精力大约是其三倍。这与这样一个事实有很大关系,即很难在组件中隔离正确的责任,然后设计一个好的API来向其他人提供功能。想出这样一个组件需要大量的脑力和反复试验。
布鲁克斯还观察到,要使组件做好重复使用的准备,还需要另外三个因素,即所需的测试、硬化和文档。最后,他指出这两个因素是独立的,即它们不相加,而是相乘。这3乘3得出了“9法则”。
你可能会说Fred Brooks对他的言论过于悲观,我们无法将70年代初的软件开发(当他发表言论时)与今天的软件开发进行比较。你可能是对的。事情已经发生了变化,可能会变得更好(尽管我还没有看到任何证据)。因此,也许9的因素在今天太高了,也许把一个简单的项目结果变成一个可重用的组件只需要5倍的努力,即为正确的责任封装、良好的API、额外的测试、文档和强化而付出额外的努力。
这意味着,为了摊销投资,您需要对一个可重复使用的组件进行至少5次的重复使用,并且您需要更高的重复使用系数来证明典型的重复使用商业案例的合理性,该案例声称您不仅可以摊销投资,而且可以随着时间的推移节省大量资金。
但是,如果你查看很少发布的SOA计划的重用数量(有趣的事实:如果你在网上搜索,你会发现大量的页面谈论重用对SOA有多重要,以及衡量重用有多重要。但你几乎找不到任何页面显示SOA计划实现的真实重用数量),你会意识到,一项服务的典型平均使用率是一点(1.x)。换句话说:大多数服务只使用过一次,少数服务(通常不到20%)使用过多次。让我们回顾一下:服务的创建成本是可重复使用的成本的五倍,平均使用次数略多于一次。这就是商业案例…
理解商业案例已经破裂是一回事。但更重要的是要认识到,重用比预期的要少得多,因此重用对任何架构范式来说都比通常声称的要重要得多。
在那句话之后,你经常会发现那些黑白相间的人会立即喊道:“所以,你告诉我们,我们根本不应该重复使用,每个人都应该重新开始写自己的字符串库!”——当然,这是胡说八道。但是,像所有极端一样,将一种说法扭曲到其绝对极端是没有意义的,这并不能证明这种说法本身是没有道理的。
关于重用的要点是,大多数可以经常重用的东西都已经实现了,因此投入精力使其可重用是有意义的。这些库通常是编程语言生态系统的一部分,无论它们是语言本身的一部分还是作为第三方库提供。就投资成本与重复使用率而言,更高级别的可重复使用组件,尤其是公司特定的业务组件,很少值得付出努力。
即使您可能拥有值得封装在可重用组件中的IT资产,也不需要新的体系结构范式:每种编程语言、每种通信协议都提供了定义接口的方法。这就是实现封装所需要的全部内容,封装是重用的基本构建块。一切都在那里,一直都在那里——不需要什么新的东西。
但我们面临着一个困境:我们想获得一种新的、闪亮的银弹式架构范式,这种范式风靡一时,因此做出了重复使用的承诺——现在我们必须实现。这通常是如何实现的?首先,我们承诺将重复使用作为我们商业案例的一部分。因此,我们在设计整个应用程序时都考虑到了重用。重复使用成为设计的驱动因素,组件必须相互重复使用,无论这是否有意义。我们承诺重复使用。因此,无论成本如何,都必须进行重复使用。
实现这一点时,我们通常会陷入一堆紧密耦合的组件中,这些组件以奇怪的方式相互使用。我们尤其没有花时间了解如何最好地设计组件,哪些边界和API会随着时间的推移而出现,哪些想法有效,哪些无效。Fred Brooks描述道,正如我们承诺的那样,重复使用将降低成本,组件需要便宜且可立即重复使用,而不需要付出努力,使其真正可重复使用。
除了其他不良影响外,这还导致了一种非常恶劣的过早优化:我们试图猜测一个组件可能以何种方式被重新使用,但由于在最初设计时我们通常对它没有真正的想法,我们只是第一次猜测并将其抛向世界。因为我们对潜在用户一无所知,所以我们只创建了一个“通用”组件,在其API中提供其内部概念。这样,我们最终得到的基本上与可重复使用的组件完全相反。
为什么我们最终会得到与可重复使用组件相反的结果?这至少有两个原因:
- 首先,由于我们不知道组件需要提供哪些可变性点来满足其客户的实际需求,我们通常会实现太多的可变性点,从而大大增加组件及其接口协议的意外复杂性。换句话说:组件变得比所需的更难使用,更难理解,整个系统的复杂性将高于所需。
- 其次,“通用”的、由提供者驱动的API往往会在提供者和客户端之间建立紧密的耦合,因为在设计时,由于您没有任何关于使用模式和客户端需求的经验,您只需要通过API将组件提供的一切具体化。这样,API是由实现驱动的,而不是由客户端驱动的,这会增加两个不良影响:
- 首先,客户端与组件的实现紧密耦合(由于在设计时缺少替代方案,通过API将其外部化),通常需要比客户端所需的更细粒度的组件访问协议。
- 其次,在客户端开始使用组件后,很难更改组件的实现,因为该实现是通过API外部化的。
- 这样,我们最终会得到一个过于复杂、紧密耦合、脆弱且难以改变的系统景观——这与我们想要实现的目标相反。即使我们最终能够根据实际客户需求制定出更好的责任封装和访问协议,我们也很难改变我们最初创建的过于复杂和高度依赖的系统。这一切都是因为我们从一个基于重用的商业案例开始,以掌握新的闪亮范式。
这不仅仅是理论。基本上,这些年来我看到的每一个考虑到重复使用的系统都和我刚才描述的一样。有时(尤其是在SOA计划中),情况甚至更糟,因为设计者选择了“分层体系结构”。
分层架构模式在一个组件内经常被打破,但在定义上是松散耦合的组件之间(因此顺便说一句,需要尽可能地自我依赖),它非常接近于可以想象到的最糟糕的设计:极度紧密耦合的组件、过长的调用链(通常跨越流程边界)、非常高的结构复杂性,由于每一个请求都涉及到许多组件,因此在运行时非常脆弱,极难更改——简而言之:从架构的角度来看,我们想要实现的目标近乎噩梦。
毕竟:我们能从中学到什么?
- 首先:永远不要把架构决策建立在重用的基础上。它永远不会起作用,你通常会在与你想去的地方完全相反的地方结束。
- 您不需要任何特殊的体系结构范式来进行重用。重复使用所需的一切都已准备就绪,包括电池。
- 再次使用出现。不要计划。设计一个好的可重复使用的组件需要大量的工作、试错和与真实客户的实际经验,该组件封装了正确的责任,并通过一个良好的客户端驱动和易于理解的API提供。
- 重复使用是昂贵的。要使一个组件可重复使用,需要付出很多努力。根据经验,它的成本大约是使组件可用的成本的5倍。因此,请确保付出的努力是值得的,也就是说,在使组件可重复使用之前,该组件的使用频率足够高。
- 最后一点:不要通过构建琐碎的组件或分层组件来进行廉价的重复使用。你最终会陷入一个你从未想过的地狱。
我们如何才能做得更好?
和往常一样,我没有一个完美的计划(可能根本没有完美的计划)。我的建议是选择可替换性,而不是重复使用。可替换性朝着正确的方向发展:您考虑如何将职责分开,使其具有可替换性,这会给您带来松散的耦合。您考虑将客户端与实现分离,以保持其可替换性,从而提供客户端驱动的API。
而且,如果您可能在一段时间后意识到,一个组件往往会被多次使用,那么您就要付出额外的努力,使其在额外的测试、文档和强化方面真正可重复使用。因此,我想在学习内容中再增加一行:
努力寻找可替换性,而不是重复使用。它会引导你走上正确的道路。
有最后的话吗?嗯,是的:别误会我的意思。重复使用本身并不坏。重复使用可能非常有用。例如,我非常感激,因为我不必像90年代初使用C和C++那样,在每个新项目中从头开始构建自己的字符串库。今天,在大多数编程语言中,创建HTTP客户端或服务器几乎是一件轻而易举的事,这也很好。这只是可能有一百万个重用效果很好的例子中的两个。
只是不要用重复使用来证明你的新架构玩具是合理的。它不会起作用。从不为什么?再次使用此帖子来了解…😉
- 14 次浏览