Imagina que tienes 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 que sea más fácil de usar, 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 a Messy
directamente. Por ejemplo, organiza administración de dependencias de tal manera que al intentar acceder a Messy
no se compila.
-
En segundo lugar, crea un módulo dedicado de "capa" que es el único que accede a Messy
y lo expone a su "código principal" de una manera que tenga más sentido para usted.
El código de la capa sería similar al siguiente:
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
, en su lugar usa Reasonable
, aproximadamente de la siguiente manera:
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 lío con Messy
, pero ahora está oculto razonablemente dentro de Reasonable
, lo que hace que su "código principal" esté razonablemente limpio y libre de daños que podrían producirse mediante el uso directo de Messy
stuff.
El ejemplo anterior se basa en cómo se explica 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 se condensa para mantener una breve explicación.
Si tiene un gran lío de API 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 material dañado directamente y segundo, expóngalo de una manera que sea más conveniente en su contexto de uso.
Al "escalar" su capa más allá del ejemplo simplificado anterior, tenga en cuenta que hacer que su API sea conveniente no es necesariamente una tarea trivial. Invierta un a diseñe su capa de la manera correcta , verifique su uso previsto con pruebas unitarias etc.
En otras palabras, asegúrese de que su API sea de hecho una mejora sobre una que oculta, asegúrese de no introducir otra capa de corrupción.
En aras de la integridad, observe la diferencia sutil pero importante entre esto y los patrones relacionados Adaptador y Facade . Como lo indica su nombre, la capa anticorrupción supone que la API subyacente tiene problemas de calidad (está" dañado ") y tiene la intención de ofrecer una protección de los problemas mencionados.
Puede pensarlo de esta manera: si puede justificar que sería mejor que el diseñador de la biblioteca exponga su funcionalidad con Reasonable
en lugar de Messy
, esto significaría que está trabajando en la capa anticorrupción, haciendo su trabajo , corrigiendo 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 la 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 los patrones como Adapter y Facade esperan que el código subyacente esté bien diseñado. Puede pensarlo de esta manera: un 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 del 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 manera estructurada y, a continuación, diseñe su adaptador / fachada sobre esa capa de protección.