ParallelChange

2014年5月13日

对影响其所有使用者的接口进行更改需要两种思维模式:实现更改本身,然后更新其所有用法。当您试图同时做这两件事时,这可能很难,特别是当更改是在PublishedInterface有多个或外部客户端。

平行变化,也被称为扩张和收缩,是一种模式,通过将更改分为三个不同的阶段:扩展、迁移和收缩,以一种安全的方式实现对接口的向后不兼容更改。

为了理解这个模式,让我们使用一个简单的例子网格类,使用一对存储并提供关于其单元格的信息xy整数坐标。单元格内部存储在一个二维数组中,客户机可以使用addCell ()fetchCell ()isEmpty ()方法与网格交互。

class Grid {private Cell[][] cells;public void addCell(int x, int y, Cell Cell) {Cell [x][y] = Cell;} public Cell fetchCell(int x, int y) {return cells[x][y];} public boolean isEmpty(int x, int y) {return cells[x][y] == null;} }

作为重构的一部分,我们188app彩票ios会检测到这一点xy是一个DataClump并决定介绍一个新的协调类。但是,这将是对客户机的向后不兼容的更改网格类。我们决定应用并行更改模式,而不是立即更改所有的方法和内部数据结构。

扩大阶段您扩充接口以同时支持旧版本和新版本。在我们的示例中,我们将引入一个新的<地图坐标,细胞>数据结构和接收的新方法协调实例,而不更改现有代码。

class Grid {private Cell[][] cells;private Map<坐标,单元格> newCells;public void addCell(int x, int y, Cell Cell) {Cell [x][y] = Cell;} public void addCell(坐标坐标,Cell Cell) {newcell。把(坐标,细胞);} public Cell fetchCell(int x, int y) {return cells[x][y];}公共Cell fetchCell(坐标坐标){返回newCells.get(坐标);} public boolean isEmpty(int x, int y) {return cells[x][y] == null;}公共boolean isEmpty(坐标坐标){返回!newCells.containsKey(坐标);} }

现有的客户机将继续使用旧版本,并且可以增量地引入新更改,而不会影响它们。

迁移将所有使用旧版本的客户端更新为新版本。这可以增量地完成,对于外部客户端,这将是最长的阶段。

将所有用法迁移到新版本后,执行合同阶段删除旧版本并更改接口,使其只支持新版本。

在我们的示例中,由于内部二维数组在旧方法被删除后不再使用,我们可以安全地删除该数据结构并重命名newCells细胞

class Grid {private Map<坐标,单元>单元;public void addCell(坐标坐标,Cell Cell) {Cell . getcell . getcell . getcell . getcell . getcell . getcell。把(坐标,细胞);}公共Cell fetchCell(坐标坐标){返回Cell .get(坐标);}公共boolean isEmpty(坐标坐标){返回!cells.containsKey(坐标);} }

这个模式在练习时特别有用ContinuousDelivery因为它允许您的代码在这三个阶段中的任何一个发布。它还允许您迁移客户端并增量地测试新版本,从而降低了更改的风险。

即使您已经控制了接口的所有用法,遵循这种模式仍然是有用的,因为它可以防止您一次性将破坏扩散到整个代码库。迁移阶段可能很短,但它是依靠编译器查找需要修复的所有用法的替代方法。

这个模式的一些示例应用程序是:

  • 188app彩票ios:当更改方法或函数签名时,尤其当执行长期的重构188app彩票ios或者改变a的时候PublishedInterface.在重构期间,此模式的变体实现是根据新API和使用实现旧方法188app彩票ios内联方法一次更新所有用法。将旧方法委托给新方法也是将迁移阶段划分为更小、更安全的步骤的一种方法,允许您在将公开的API更改给客户端之前先更改内部实现。当迁移阶段较长,因此您不必维护两个独立的实现时,这很有用。
  • 数据库重构188app彩票ios:这是一个关键的组成部分进化的数据库设计.大多数数据库重构都遵循并行更188app彩票ios改模式,其中迁移阶段是原始模式和新模式之间的过渡阶段,直到更新所有数据库访问代码以使用新模式为止。
  • 部署:部署技术,如金丝雀发布和BlueGreenDeployment是并行变更模式的应用程序,其中您将代码的新旧版本并排部署,并且您增量地将用户从一个版本迁移到另一个版本,因此降低了变更的风险。在一个microservices188比分直播网 坚持原创在体系结构方面,它还可以消除由于不同服务之间的版本依赖关系而对不同服务进行复杂的部署编排的需要。
  • 远程API演化:当你不能以向后兼容的方式进行更改时,并行更改可以用来发展远程API(例如,一个REST web服务)。这是在公开的API中使用显式版本的另一种选择。当对给定端点上的API接受或返回的有效负载进行更改时,您可以应用该模式,或者您可以引入一个新端点来区分新旧版本。在同一端点使用并行更改的情况下,如下Postel法则是一种避免在扩展有效负载时消费者中断的好技术。

在迁移阶段,aFeatureToggle可以用来控制使用哪个版本的接口。客户端上的特性切换允许它与供应商的新版本向前兼容,这将供应商的发布与客户端分离。

在实现BranchByAbstraction,并行变更是在客户端和供应商之间引入抽象层的好方法。它也是执行大规模更改的另一种方法,而不引入抽象层作为供应商端替换的接缝。然而,当您有大量的客户端时,使用抽象分支是一种更好的策略,可以在迁移阶段缩小变化的范围并减少混淆。

使用并行更改的缺点是,在迁移阶段,供应商必须支持两个不同的版本,客户可能会弄不清哪个版本是新版本还是旧版本。如果契约阶段没有执行,您可能最终处于比开始时更糟糕的状态,因此您需要规范来成功完成转换。添加弃用说明、文档或TODO说明可能有助于告知客户端和其他在相同代码基上工作的开发人员哪个版本正在被替换。

进一步的阅读

工业逻辑188app彩票ios重构专辑文档并演示了一个执行并行更改的示例。

确认

Joshua Kerievsky在2006年将该技术作为一种重构策略首次记录在案,188app彩票ios并在他的演讲中提出有限红色社团在2010年的精益软件和系统会议上提出。

感谢Joshua Kerievsky对本文初稿的反馈。也感谢Thoughtworks的许多同事金宝搏亚洲体育博彩提供的反馈:Greg Dutcher、Badrinath Janakiraman、Praful Todkar、Rick Carragher、Filipe Esperandio、Jason Yip、Tushar Madhukar、Pete Hodgson和Kief Morris。