Imagine que você precise usar o código de outra pessoa que é projetado como 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 que depende dele se parece com o 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ê deseja 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, então você começa a criar uma camada anticorrupção.
A primeira coisa é garantir que o seu "código principal" não se refira Messy
diretamente. Por exemplo, você organiza o gerenciamento de dependências de tal maneira que a tentativa de acessar Messy
falha na compilação.
Segundo, você cria um módulo de "camada" dedicado, que é o único que acessa Messy
e o expõe ao seu "código principal" de uma maneira que faça mais sentido para você.
O código da camada teria a seguinte aparência:
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, o seu "código principal" não mexe Messy
, usando em Reasonable
vez disso, da seguinte maneira:
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á um pouco de confusão, Messy
mas isso agora está razoavelmente escondido no fundo Reasonable
, tornando seu "código principal" razoavelmente limpo e livre de corrupção que seria trazida para lá pelo uso direto de Messy
coisas.
O exemplo acima é baseado em como a Camada Anticorrupção é explicada no c2 wiki:
Se seu aplicativo precisar lidar com um banco de dados ou outro aplicativo cujo modelo seja indesejável ou inaplicável ao modelo que você deseja em seu próprio aplicativo, use um AnticorruptionLayer para converter de / para esse modelo e o seu.
O exemplo de nota é intencionalmente simplificado e condensado para manter a explicação breve.
Se você tem uma bagunça de API maior para cobrir a camada anticorrupção, aplica-se a mesma abordagem: primeiro, verifique se o seu "código principal" não acessa diretamente itens corrompidos e depois, exponha-o de uma maneira que seja mais conveniente no seu contexto de uso.
Ao "escalar" sua camada além do exemplo simplificado acima, leve em consideração que tornar sua API conveniente não é necessariamente uma tarefa trivial. Invista um esforço para projetar sua camada da maneira correta , verifique seu uso pretendido com testes de unidade etc.
Em outras palavras, verifique se a sua API é realmente uma melhoria sobre a que oculta, e não apenas introduza outra camada de corrupção.
Por uma questão de integridade, observe uma diferença sutil, mas importante, entre este e os padrões relacionados Adapter e Facade . Conforme indicado pelo nome, a camada anticorrupção pressupõe que a API subjacente tenha problemas de qualidade (está "corrompida") e pretende oferecer uma proteção dos problemas mencionados.
Você pode pensar dessa maneira: se você puder justificar que o designer da biblioteca seria melhor expor sua funcionalidade com, em Reasonable
vez de Messy
, isso significa que você está trabalhando na camada anticorrupção, realizando seu trabalho, corrigindo seus erros de design.
Ao contrário disso, o Adapter e o Facade não fazem suposições sobre a qualidade do design subjacente. Eles podem ser aplicados à API bem projetada para começar, apenas adaptando-a às suas necessidades específicas.
Na verdade, pode ser ainda mais produtivo supor que padrões como o Adapter e o Facade esperam que o código subjacente seja bem projetado. Você pode pensar desta maneira: 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 demorar mais do que o esperado, isso pode indicar que o código subjacente está, de alguma forma, "corrompido". Nesse caso, você pode considerar a divisão do trabalho em fases separadas: primeiro, estabeleça uma camada anticorrupção para apresentar a API subjacente de uma maneira adequadamente estruturada e, a seguir, projete seu adaptador / fachada sobre essa camada de proteção.