Imagine que você precisa usar o código de outra pessoa projetado conforme mostrado abaixo:
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) { /* ... */ }
}
Agora imagine que você descubra que seu código depende do seguinte:
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?
}
... e que você quer facilitar o uso, em particular, para se livrar do uso repetitivo de parâmetros que simplesmente não são necessários para o seu aplicativo.
Ok, você começa a criar uma camada anticorrupção.
-
A primeira coisa é garantir que o seu "código principal" não se refira a
Messy
diretamente. Por exemplo, você organiza o gerenciamento de dependências de tal forma que tente acessarMessy
falha na compilação. -
Em segundo lugar, você cria um módulo "camada" dedicado que é o único que acessa
Messy
e o expõe ao seu "código principal" de uma maneira que faz mais sentido para você.
O código da camada seria semelhante ao seguinte:
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); }
}
Como resultado, seu "código principal" não é confundido com Messy
, usando Reasonable
da seguinte forma:
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");
}
Note que ainda há uma bagunça mexendo com Messy
, mas isso está escondido razoavelmente dentro de Reasonable
, tornando seu "código principal" razoavelmente limpo e livre de corrupção que seria trazido para lá pelo uso direto de Messy
stuff.
O exemplo acima é baseado em como a Camada Anticorrupção é explicada no wiki c2:
If your application needs to deal with a database or another application whose model is undesirable or inapplicable to the model you want within your own application, use an AnticorruptionLayer to translate to/from that model and yours.
O exemplo de nota é intencionalmente simples e condensado para manter a explicação breve.
Se você tem uma grande confusão de API para cobrir a camada anticorrupção, a mesma abordagem se aplica: primeiro, certifique-se de que seu "código principal" não acesse material corrompido diretamente e segundo, exponha-o de uma maneira que seja mais conveniente em seu contexto de uso.
Quando "dimensionar" sua camada além de um exemplo simplificado acima, leve em consideração que tornar sua API conveniente não é necessariamente uma tarefa trivial. Invista um esforço para projete sua camada da maneira certa , verifique se uso pretendido com testes unitários etc.
Em outras palavras, certifique-se de que sua API seja de fato uma melhoria sobre a que ela oculta. Certifique-se de não introduzir apenas outra camada de corrupção.
Por uma questão de completude, observe a diferença sutil, mas importante, entre este e os padrões relacionados Adaptador e Facade . Como indicado pelo nome, a camada anticorrupção assume que a API subjacente tem problemas de qualidade (é" corrompido ") e pretende oferecer uma proteção dos problemas mencionados.
Você pode pensar desta forma: se você puder justificar que o designer de bibliotecas seria melhor expor sua funcionalidade com Reasonable
em vez de Messy
, isso significaria que você está trabalhando na camada anticorrupção, fazendo trabalho deles, corrigindo os erros de design deles .
Ao contrário disso, Adaptador e Fachada não fazem suposições sobre a qualidade do design subjacente. Isso pode ser aplicado à API que foi bem projetada para começar, apenas adaptando-a para suas necessidades específicas.
Na verdade, pode até ser mais produtivo supor que padrões como Adaptador e Fachada esperem que o código subjacente seja bem projetado. Você pode pensar assim: código bem projetado não deve ser muito difícil de ajustar para um caso de uso específico. Se o design do seu adaptador exigir mais esforço do que o esperado, isso pode indicar que o código subjacente está, de alguma forma, "corrompido". Nesse caso, você pode considerar dividir o trabalho em fases separadas: primeiro, estabelecer uma camada anticorrupção para apresentar a API subjacente de maneira adequadamente estruturada e, em seguida, projetar seu adaptador / fachada sobre essa camada de proteção.