Imagine que tiene que usar el código de otra persona que está diseñado como se muestra a continuación:
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) { /* ... */ }
}
Ahora imagine que descubre que su código que depende de él se parece a lo siguiente:
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?
}
... y que desea facilitar su uso, en particular, para deshacerse del uso repetitivo de parámetros que simplemente no son necesarios para su aplicación.
Bien, entonces comienzas a construir una capa anticorrupción.
Lo primero es asegurarse de que su "código principal" no se refiera Messy
directamente. Por ejemplo, organiza la gestión de dependencias de tal manera que al intentar acceder Messy
no se compila.
En segundo lugar, crea un módulo de "capa" dedicado que es el único que accede Messy
y lo expone a su "código principal" de una manera que tenga más sentido para usted.
El código de capa se vería así:
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, su "código principal" no se mete con Messy
, utilizando en su Reasonable
lugar, aproximadamente lo siguiente:
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");
}
Tenga en cuenta que todavía hay un poco de desorden con Messy
esto, pero ahora está oculto razonablemente en el fondo Reasonable
, haciendo que su "código principal" sea razonablemente limpio y libre de corrupción que se llevaría allí por el uso directo de Messy
cosas.
El ejemplo anterior se basa en cómo se explica la capa anticorrupción en c2 wiki:
Si su aplicación necesita tratar con una base de datos u otra aplicación cuyo modelo no es deseable o no es aplicable al modelo que desea dentro de su propia aplicación, use un AnticorruptionLayer para traducir a / desde ese modelo y el suyo.
El ejemplo de la nota se hace intencionalmente simple y condensado para mantener la explicación breve.
Si tiene un desorden de API más grande para cubrir detrás de la capa anticorrupción, se aplica el mismo enfoque: primero, asegúrese de que su "código principal" no acceda a cosas corruptas directamente y segundo, exponga de una manera que sea más conveniente en su contexto de uso.
Al "escalar" su capa más allá de un ejemplo simplificado anterior, tenga en cuenta que hacer que su API sea conveniente no es necesariamente una tarea trivial. Invierta un esfuerzo para diseñar su capa de la manera correcta , verifique su uso previsto con pruebas unitarias, etc.
En otras palabras, asegúrese de que su API sea realmente una mejora sobre la que oculta, asegúrese de no solo introducir otra capa de corrupción.
En aras de la exhaustividad, observe la diferencia sutil pero importante entre este y los patrones relacionados Adaptador y Fachada . Como lo indica su nombre, la capa anticorrupción supone que la API subyacente tiene problemas de calidad (está "corrupta") y tiene la intención de ofrecer una protección de los problemas mencionados.
Puede pensarlo de esta manera: si puede justificar que el diseñador de la biblioteca estaría mejor exponiendo su funcionalidad en Reasonable
lugar de Messy
, esto significaría que está trabajando en la capa anticorrupción, haciendo su trabajo, reparando sus errores de diseño.
A diferencia de eso, Adapter y Facade no hacen suposiciones sobre la calidad del diseño subyacente. Estos podrían aplicarse a una API que está bien diseñada para comenzar, simplemente adaptándola a sus necesidades específicas.
En realidad, incluso podría ser más productivo suponer que patrones como Adaptador y Fachada esperan que el código subyacente esté bien diseñado. Puede pensarlo de esta manera: el código bien diseñado no debería ser demasiado difícil de modificar para un caso de uso particular. Si resulta que el diseño de su adaptador requiere más esfuerzo de lo esperado, esto podría indicar que el código subyacente está, de alguna manera, "dañado". En ese caso, puede considerar dividir el trabajo en fases separadas: primero, establezca una capa anticorrupción para presentar la API subyacente de una manera adecuadamente estructurada y luego, diseñe su adaptador / fachada sobre esa capa de protección.