什么是反腐败层?如何使用?


151

我正在尝试弄清“反腐败”层的真正含义。我知道这是一种过渡/解决旧代码或错误API的方法。我不明白的是它是如何工作的,是什么使它与不良层完全分离。

我已经进行了一些搜索,但是找不到任何简单的示例或解释,因此我正在寻找可以理解并可以通过简单示例进行解释的人员。可以满足我的问题的答案应该很简单(不一定要简短),并提供易于理解的实现和使用示例。

有关我的用例,请参见此问题

Answers:


147

假设您必须使用如下所示设计的其他人的代码:

    class Messy {
        String concat(String param, String str) { /* ... */ }
        boolean contains(String param, String s) { /* ... */ }
        boolean isEmpty(String param) { /* ... */ }
        boolean matches(String param, String regex) { /* ... */ }
        boolean startsWith(String param, String prefix) { /* ... */ }
    }

现在,假设您发现依赖于它的代码如下所示:

String process(String param) {
    Messy messy = new Messy();
    if (messy.contains(param, "whatever")) {
        return messy.concat(param, "-contains");
    }
    if (messy.isEmpty(param)) {
        return messy.concat(param, "-empty");
    }
    if (messy.matches(param, "[whatever]")) {
        return messy.concat(param, "-matches");
    }
    if (messy.startsWith(param, "whatever")) {
        return messy.concat(param, "-startsWith");
    }
    return messy.concat(param, "-whatever");
    // WTF do I really need to repeat bloody "param" 9 times above?
}

...并且您想使其更易于使用,尤其是摆脱对应用程序不需要的参数的重复使用。

好的,因此您开始构建反腐败层。

  1. 第一件事是确保您的“主代码”没有Messy直接引用。例如,以某种方式安排依赖项管理,以使尝试访问Messy无法编译。

  2. 其次,创建一个专用的“层”模块,该模块是唯一一种Messy以对您更有意义的方式访问并将其公开给您的“主代码”的模块。

层代码如下所示:

    class Reasonable { // anti-corruption layer
        String param;
        Messy messy = new Messy();
        Reasonable(String param) {
            this.param = param;
        }
        String concat(String str) { return messy.concat(param, str); }
        boolean contains(String s) { return messy.contains(param, s); }
        boolean isEmpty() { return messy.isEmpty(param); }
        boolean matches(String regex) { return messy.matches(param, regex); }
        boolean startsWith(String prefix) { return messy.startsWith(param, prefix); }
    }

结果,您的“主代码”不会与混乱MessyReasonable而是使用大约如下的方式:

String process(String param) {
    Reasonable reasonable = new Reasonable(param);
    // single use of "param" above and voila, you're free
    if (reasonable.contains("whatever")) {
        return reasonable.concat("-contains");
    }
    if (reasonable.isEmpty()) {
        return reasonable.concat("-empty");
    }
    if (reasonable.matches("[whatever]")) {
        return reasonable.concat("-matches");
    }
    if (reasonable.startsWith("whatever")) {
        return reasonable.concat("-startsWith");
    }
    return reasonable.concat("-whatever");
}

请注意,仍然有些混乱,Messy但是现在它已经隐藏在相当深的内部Reasonable,使您的“主代码”相当干净并且没有直接使用Messy东西会带来的损坏。


上面的示例基于c2 wiki 如何解释“ 反腐败层”

如果您的应用程序需要处理其模型在您自己的应用程序中不想要或不适用于您想要的模型的数据库或其他应用程序,请使用AnticorruptionLayer与该模型和您的模型进行相互转换。

注释示例特意简化并压缩以使说明简短。

如果您需要在反腐败层后面覆盖较大的API混乱,则可以采用相同的方法:首先,确保您的“主代码”不直接访问已损坏的内容,其次,以一种更多的方式公开它在您的使用环境中很方便。

当将层“扩展”到上面的简化示例之外时,请考虑使API变得方便不一定是一项琐碎的任务。投资的努力设计你的层以正确的方式,验证它的预期用途的单元测试等。

换句话说,请确保您的API确实是对它所隐藏的API的改进,并确保您不只是引入了另一层损坏。


为了完整起见,请注意此模式和相关模式AdapterFacade之间的细微但重要的区别。顾名思义,反腐败层假定底层 API存在质量问题(“被破坏”),并打算对上述问题提供保护。

你可以认为它是这样的:如果你能证明该库设计师将关闭与暴露它的功能更好的Reasonable替代Messy,这将意味着你的工作反腐层上,做他们的工作,固定设计错误。

与此相反,AdapterFacade不对基础设计的质量进行假设。这些可以应用于经过精心设计的API,只需将其适应您的特定需求即可。

实际上,假设诸如AdapterFacade之类的模式期望底层代码经过精心设计,可能会更有效率。您可以这样想:精心设计的代码针对特定用例进行调整应该不会太困难。如果事实证明,适配器的设计比预期的花费更多的精力,则可能表明底层代码以某种方式“损坏”。在这种情况下,您可以考虑将工作分为几个阶段:首先,建立一个反腐败层,以适当的结构化方式呈现底层API,然后,在该保护层上设计适配器/外观。


1
如果存在依赖的API类的整体结构,该如何缩放?它是否比保护其他应用程序的层更易于管理?
knownasilya

1
@Knownasilya这是一个很好的问题,答案扩大到解决
蚊蚋

4
In other words, make sure that your API is indeed an improvement over one it hides, make sure that you don't just introduce another layer of corruption.整个部分值得大胆使用。
Lilienthal 2015年

19
反腐败层与处理质量差的API无关。它们是关于解决概念上的不匹配,通过仅将代码“破坏”我们可以更轻松使用的域来适应我们只能使用的域。
伊恩·费尔曼

8
伊恩·费尔曼(Ian Fairman)的回答正确,而该答案的作者绝对没有。如果您从概念的源头(DDD书)中发现,至少有两点与该答案相矛盾:1)创建了一个反腐败层,以避免破坏我们正在使用元素开发新域模型。根据现有外部系统的模型;这并不是说另一个系统“被破坏了”,实际上,它可能是完美的,精心设计的;2)反腐败通常包含几个类,通常包括 FacadesAdapters以及Services
罗杰里奥

41

引用其他来源:

创建一个隔离层,以根据客户端自己的域模型为客户提供功能。该层通过其现有接口与另一个系统进行通信,几乎不需要或不需要对其进行任何修改。在内部,该层在两个模型之间根据需要在两个方向上平移。

埃里克·埃文斯(Eric Evans),《域驱动设计》,第16版,第365页

最重要的是,在反腐败层的每一侧使用不同的术语。我曾经在一个运输物流系统上工作。必须计划好回合。您必须将车辆装备在仓库中,开车到不同的客户站点并为他们提供服务,并访问其他地方,例如油罐站。但是从更高的角度来看,这全都与任务计划有关。因此,将更一般的任务计划术语与非常具体的运输物流术语分开是有意义的。

因此,反腐败层隔离不仅是为了保护您免受混乱的代码的侵害,还在于分离不同的域并确保它们在将来保持分离。


6
这个非常重要!ACL不仅可以与Messy代码一起使用,而且还可以作为在受限上下文之间进行通信的手段。它从一个上下文转换到另一个上下文,以便每个上下文中的数据都反映该上下文思考和讨论数据的语言和方式。
Didier A.

29

适配器

当您具有不兼容的接口时,它们会执行类似的逻辑,以使一个接口适应另一个接口,以便您可以将其中一个的实现与期望另一个的实现一起使用。

例:

您有一个需要Car的对象,但是只有4WheelVehicle类,因此您创建了CarBuiltUsing4WheelVehicle并将其用作您的Car。

正面

当您使用复杂/混乱/庞大的API时,希望使其更简单/更清晰/更小。您将创建一个Facade以隐藏复杂性/混乱/附加内容,仅公开新的简单/清晰/小型API。

例:

您正在使用一个具有100个方法的库,要执行某些任务,您需要进行一堆初始化,连接,打开/关闭操作,最后才能够执行您想要的操作,而您想要的只是其中一项功能库可以完成全部50项操作,因此您创建的Facade仅具有所需功能的一种方法,并且可以为您完成所有初始化,清理等工作。

反腐败层

当您的系统不在您的域中时,而您的业务需求则要求您使用该其他域。您不想将另一个域引入您自己的域中,因此会破坏它,因此您会将域的概念转换为另一个域,反之亦然。

例:

一个系统查看具有名称和字符串列表的客户,每笔交易一个。您将“个人档案”视为具有名称的独立类,将“交易”视为具有字符串的独立类,将“客户”视为具有个人档案和交易的集合。

因此,您将创建一个ACL层,该层将允许您在客户与另一个系统的客户之间进行转换。这样,您不必使用其他系统的客户,只需告诉ACL:“将Profile X给我客户,然后ACL告诉另一个系统给它一个名为X.name的客户,然后返回您是配置文件X的客户。

====================

这三个都是相对相似的,因为它们都是间接模式。但是它们解决了不同的结构,类/对象与API,模块/子系统。如果需要,可以将它们全部合并。子系统具有复杂的API,因此您需要为它构建一个FACADE,它使用不同的模型,因此对于每个不适合您模型的数据表示形式,您都需要将该数据转换回建模的方式。最后,也许接口也不兼容,所以您将使用ADAPTERS来相互适应。


12

这里有很多答案说ACL不仅仅是包装混乱的代码。我会走得更远,说他们根本不关心这个,如果这样做,这是附带利益。

反腐败层是将一个域映射到另一个域,这样使用第二个域的服务就不必被第一个域的概念“破坏”。对于域模型,ACL是针对类的适配器,只是在不同级别上发生。适配器可以说是最重要的设计模式-我一直都在使用它-但是判断包装好的类是否杂乱无关紧要。就是这样,我只需要它具有不同的接口即可。

专注于混乱是一种误导,并错过了DDD的含义。ACL是关于处理概念上的不匹配,而不是质量较差的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.