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.
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.
Toda esa sección es digna de una etiqueta en negrita.Para citar otra fuente:
Eric Evans, Diseño dirigido por dominio, 16a impresión, página 365
Lo más importante es que se utilizan diferentes términos a cada lado de la capa anticorrupción. Una vez estuve trabajando en un sistema de logística de transporte. Las rondas tuvieron que ser planificadas. Debía equipar el vehículo en un depósito, conducir a diferentes sitios de clientes y darles servicio y visitar otros lugares, como una parada de tanques. Pero desde el nivel superior, todo se trataba de planificación de tareas. Por lo tanto, tenía sentido separar los términos más generales de planificación de tareas de los términos logísticos de transporte muy específicos.
Por lo tanto, un aislamiento de capas anticorrupción no se trata solo de protegerlo del código desordenado, sino de separar diferentes dominios y asegurarse de que permanezcan separados en el futuro.
fuente
Adaptador
Cuando tiene interfaces incompatibles, que realizan una lógica similar, para adaptarse una a la otra, de modo que pueda usar implementaciones de una con cosas que esperan la otra.
Ejemplo:
Tiene un objeto que quiere un automóvil, pero solo tiene una clase 4WheelVehicle, por lo que crea un CarBuiltUsing4WheelVehicle y lo usa como su automóvil.
Fachada
Cuando tiene una API compleja / confusa / gigantesca y desea simplificarla / aclararla / reducirla. Creará una Fachada para ocultar la complejidad / confusión / extras y solo expondrá una nueva API simple / clara / pequeña.
Ejemplo:
Está utilizando una biblioteca que tiene 100 métodos, y para realizar cierta tarea necesita hacer un montón de inicialización, conexión, apertura / cierre de cosas, solo para finalmente poder hacer lo que quería, y todo lo que quería es 1 característica de los 50 que puede hacer la biblioteca, por lo que crea una Fachada que solo tiene un método para esa característica que necesita y que realiza toda la inicialización, limpieza, etc.
Capa anticorrupción
Cuando tiene un sistema que está fuera de su dominio, sin embargo, las necesidades de su negocio requieren que trabaje con ese otro dominio. No desea introducir este otro dominio en el suyo propio, por lo tanto, corromperlo, por lo que traducirá el concepto de su dominio, a este otro dominio, y viceversa.
Ejemplo:
Un sistema ve al cliente que tiene un nombre y una lista de cadenas, una para cada transacción. Usted ve los Perfiles como clases independientes que tienen un nombre, y las Transacciones como clases independientes que tienen una cadena, y el Cliente tiene un Perfil y una colección de Transacciones.
Por lo tanto, crea una capa de ACL que permitirá traducir entre su Cliente y el Cliente del otro sistema. De esta manera, nunca tiene que usar el Cliente del otro sistema, simplemente debe decirle a la ACL: "deme el Cliente con el Perfil X, y la ACL le dice al otro sistema que le dé un Cliente con el nombre X.name, y devuelve usted un cliente con perfil X.
====================
Los tres son relativamente similares, porque todos son patrones de indirección. Pero abordan diferentes estructuras, clases / objetos versus API versus módulos / subsistemas. Podrías combinarlos todos si fuera necesario. El subsistema tiene una API compleja, por lo que construye una FACHADA para él, utiliza un modelo diferente, por lo que para cada representación de datos que no se ajuste a su modelo, VOLVERÁ A TRADUCIR esos datos a la forma en que lo modela. Finalmente, tal vez las interfaces también sean incompatibles, por lo que usaría ADAPTADORES para adaptarse de uno a otro.
fuente
Muchas respuestas aquí dicen que las ACL "no son solo" acerca de envolver código desordenado. Yo iría más lejos y diría que no se trata de eso en absoluto, y si lo hacen, eso es un beneficio adicional.
Una capa anticorrupción se trata de mapear un dominio sobre otro para que los servicios que usan el segundo dominio no tengan que estar "corrompidos" por los conceptos del primero. Las ACL son para modelos de dominio lo que los adaptadores son para las clases, simplemente está sucediendo en un nivel diferente. Podría decirse que el adaptador es el patrón de diseño más importante, lo uso todo el tiempo, pero juzgar que la clase envuelta es desordenada o no es irrelevante. Es lo que es, solo necesito que tenga una interfaz diferente.
Centrarse en el desorden es engañoso y se pierde el punto de lo que se trata DDD. Las ACL tratan sobre desajustes conceptuales, no de baja calidad.
fuente