我正在尝试弄清“反腐败”层的真正含义。我知道这是一种过渡/解决旧代码或错误API的方法。我不了解的是它是如何工作的,是什么使它与不良层完全分离。我正在寻找一个了解它并可以通过简单示例解释它的人。可以满足我的问题的答案应该很简单(不一定简短),并提供易于理解的实现和使用示例。

#1 楼

想象一下,您必须使用如下所示设计的其他人的代码:



     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?
}
 


...并且您想使其更易于使用,尤其是摆脱对应用程序不需要的参数的重复使用。 >好吧,所以您开始构建反腐败层。


第一件事是确保您的“主代码”没有直接引用Messy。例如,以某种方式安排依赖项管理,以使尝试访问Messy的程序无法编译。
其次,创建一个专用的“层”模块,该模块是唯一访问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); }
    }
 Messy混淆,而是使用Reasonable,大致如下:

 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的东西可能会导致损坏。 >如果您的应用程序需要处理模型不适合您自己的应用程序或您自己的应用程序中所需模型的数据库或其他应用程序,请使用AnticorruptionLayer来与该模型和您的模型进行相互转换。


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

如果您需要在反腐败层后面覆盖较大的API混乱,则可以采用相同的方法:首先,确保您的“主代码”不直接访问损坏的内容,其次,以一种更多在使用上下文中方便使用。请投入精力以正确的方式设计您的层,并通过单元测试等来验证其预期用途。换句话说,请确保您的API确实比其隐藏的API有所改进,请确保您不要仅仅引入另一层损坏。


为了完整性,请注意此模式和相关模式Adapter和Facade之间的细微但重要的区别。顾名思义,反腐败层假定底层API存在质量问题(“已损坏”),并打算对上述问题提供保护。有理由证明,库设计人员最好通过Reasonable而不是Messy公开其功能,这意味着您正在反腐败层上,在完成他们的工作,解决他们的设计错误。 Adapter和Facade不对基础设计的质量进行假设。这些可以应用于经过精心设计的API,只需将其适应您的特定需求即可。

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

评论


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

–knownasilya
13年1月22日在18:12

@Knownasilya这是一个很好的问题,答案可以扩展为解决该问题

– gna
13年1月22日在18:36

换句话说,请确保您的API确实是对它所隐藏的API的改进,并确保您不只是引入了另一层损坏。整个部分值得大胆使用。

– Liilienthal
15年1月29日在22:30

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

–伊恩·费尔曼(Ian Fairman)
16 Jun 10'在10:17



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

–罗杰里奥
16年8月24日在1:39

#2 楼

引用另一个来源:


创建一个隔离层,以客户端自己的域模型的方式为客户提供功能。该层通过其现有接口与另一个系统进行通信,而无需或几乎不需要对另一个系统进行任何修改。在内部,该层会在两个模型之间根据需要在两个方向上进行平移。 >最重要的是,反腐败层的每一侧使用不同的术语。我曾经在一个运输物流系统上工作。必须计划好回合。您必须将车辆装备在仓库中,开车到不同的客户地点并为他们提供服务,并参观其他地方,例如油罐站。但是从更高的角度来看,这全都与任务计划有关。因此,将更一般的任务计划术语与非常具体的运输后勤术语分开是很有意义的。并确保它们在将来保持隔离。

评论


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

– DiidierA。
2014年11月14日在1:43

#3 楼

适配器

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

示例:

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

Facade

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

示例:

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

反腐败层

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

示例:

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

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

================“ =它们都是间接模式,但是它们解决的是不同的结构,类/对象,API,模块/子系统,如果需要的话,可以将它们组合在一起。它使用不同的模型,因此对于每个不适合您模型的数据表示形式,您都需要将该数据转换回建模的方式中,最后,接口也可能不兼容,因此您可以使用ADAPTERS来适应一个到另一个。

#4 楼

这里有很多答案说ACL不仅仅是包装混乱的代码。我会走得更远,说它们根本不关乎这个,如果这样做的话,这是一个附带好处。

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

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