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.
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.
Essa seção inteira é digna de uma marca em negrito.Para citar outra fonte:
Eric Evans, Domain Driven Design, 16ª impressão, página 365
O mais importante é que termos diferentes sejam usados em cada lado da camada anticorrupção. Eu estava trabalhando em um sistema de logística de transporte. Rodadas tiveram que ser planejadas. Você tinha que equipar o veículo em um depósito, dirigir para diferentes locais de clientes, atendê-los e visitar outros lugares, como uma parada de tanque. Mas, do nível superior, tudo isso era sobre planejamento de tarefas. Portanto, fazia sentido separar os termos mais gerais de planejamento de tarefas dos termos muito específicos da logística de transporte.
Portanto, um isolamento de camadas anticorrupção não é apenas proteger você de códigos confusos, é separar domínios diferentes e garantir que eles fiquem separados no futuro.
fonte
Adaptador
Quando você possui interfaces incompatíveis, que executam lógica semelhante, para adaptar uma à outra, para que você possa usar implementações de uma com coisas que esperam a outra.
Exemplo:
Você tem um objeto que deseja um carro, mas apenas uma classe 4WheelVehicle, então cria um CarBuiltUsing4WheelVehicle e o usa como seu carro.
Fachada
Quando você tem uma API complexa / confusa / gigantesca e deseja torná-la mais simples / clara / menor. Você criará uma fachada para ocultar a complexidade / confusão / extras e apenas expor uma nova API simples / clara / pequena.
Exemplo:
Você está usando uma biblioteca que possui 100 métodos e, para executar determinadas tarefas, é necessário executar várias tarefas de inicialização, conexão, abertura / fechamento, apenas para finalmente poder fazer o que você queria, e tudo o que você queria era um recurso do todas as 50 bibliotecas podem fazer, então você cria uma Fachada que possui apenas um método para o recurso 1 necessário e que faz toda a inicialização, limpeza etc. para você.
Camada anticorrupção
Quando você tem um sistema fora do seu domínio, as necessidades da sua empresa exigem que você trabalhe com esse outro domínio. Você não deseja introduzir esse outro domínio em seu próprio país, corrompendo-o, portanto, converterá o conceito de seu domínio nesse outro domínio e vice-versa.
Exemplo:
Um sistema exibe o cliente com um nome e uma lista de cadeias, uma para cada transação. Você vê Perfis como classes independentes com um nome e Transações como classes independentes com uma cadeia de caracteres e Customer como tendo um Perfil e uma coleção de Transações.
Então, você cria uma camada ACL que permitirá a conversão entre o cliente e o cliente do outro sistema. Dessa forma, você nunca precisará usar o Cliente do outro sistema, basta informar à ACL: "dê-me o Cliente com o Perfil X, e a ACL instrui o outro sistema a fornecer a um Cliente com o nome X.name e retorna você é um cliente com perfil X.
====================
Todos os três são relativamente semelhantes, porque são todos padrões de indireção. Mas eles abordam diferentes estruturas, classes / objetos versus APIs versus módulos / subsistemas. Você poderia combiná-los todos, se necessário. O subsistema possui uma API complexa; portanto, você cria um FACADE para ele, usa um modelo diferente; portanto, para cada representação de dados que não se encaixa no seu modelo, você TRANSLATE esses dados de volta para a forma como o modela. Finalmente, talvez as interfaces também sejam incompatíveis, portanto você usaria ADAPTERS para se adaptar de um para o outro.
fonte
Muitas respostas aqui dizem que as ACLs "não são apenas" sobre quebra de código confuso. Eu iria além e diria que eles não têm nada a ver com isso, e se o fizerem, esse é um benefício colateral.
Uma camada anticorrupção é sobre o mapeamento de um domínio para outro, para que os serviços que usam o segundo domínio não precisem ser "corrompidos" pelos conceitos do primeiro. As ACLs são para modelos de domínio o que são adaptadores para classes, isso está acontecendo em um nível diferente. O adaptador é sem dúvida o padrão de design mais importante - eu o uso o tempo todo -, mas julgar a classe finalizada como desarrumada ou não é irrelevante. É o que é, eu só preciso que ele tenha uma interface diferente.
Concentrar-se na bagunça é enganoso e perde o objetivo do DDD. As ACLs tratam de incompatibilidades conceituais, não de baixa qualidade.
fonte