Imaginez que vous deviez utiliser le code de quelqu'un d'autre conçu comme indiqué ci-dessous:
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) { /* ... */ }
}
Maintenant, imaginez que vous découvriez que votre code qui en dépend ressemble à ceci:
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?
}
... et que vous souhaitez faciliter l'utilisation, en particulier, pour vous débarrasser de l'utilisation répétitive de paramètres qui ne sont tout simplement pas nécessaires pour votre application.
Bon, vous commencez à construire une couche anti-corruption.
La première chose à faire est de vous assurer que votre "code principal" ne fait pas Messy
directement référence . Par exemple, vous organisez la gestion des dépendances de telle manière que la tentative d'accès Messy
échoue lors de la compilation.
Deuxièmement, vous créez un module "couche" dédié qui est le seul à y accéder Messy
et vous l'exposez à votre "code principal" de manière plus logique.
Le code de couche ressemblerait à ceci:
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); }
}
En conséquence, votre "code principal" ne joue pas avec l' Messy
utilisation Reasonable
suivante:
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");
}
Notez qu'il y a encore un peu de gâchis Messy
mais cela est maintenant caché assez profondément à l'intérieur Reasonable
, rendant votre "code principal" raisonnablement propre et exempt de corruption qui y serait apporté par l'utilisation directe de Messy
choses.
L'exemple ci-dessus est basé sur la façon dont la couche anticorruption est expliquée sur le wiki c2:
Si votre application doit traiter avec une base de données ou une autre application dont le modèle est indésirable ou inapplicable au modèle souhaité dans votre propre application, utilisez un AnticorruptionLayer pour effectuer la traduction vers / à partir de ce modèle et le vôtre.
Remarque exemple est volontairement simplifié et condensé pour garder l'explication brève.
Si vous avez un plus grand gâchis d’API à couvrir derrière la couche anti-corruption, la même approche s’applique: d’abord, assurez-vous que votre "code principal" n’accède pas directement au contenu corrompu et, deuxièmement, exposez-le de manière plus efficace. pratique dans votre contexte d'utilisation.
Lorsque vous "redimensionnez" votre couche au-delà de l'exemple simplifié ci-dessus, prenez en compte le fait que rendre votre API pratique n'est pas nécessairement une tâche triviale. Faites un effort pour concevoir votre couche de la bonne manière , vérifiez son utilisation prévue avec des tests unitaires, etc.
En d'autres termes, assurez-vous que votre API est bien une amélioration par rapport à celle qu'elle cache, assurez-vous de ne pas simplement introduire une autre couche de corruption.
Par souci d'exhaustivité, notez la différence subtile mais importante entre ce modèle et les modèles associés Adaptateur et Façade . Comme son nom l’indique, la couche anticorruption suppose que l’ API sous - jacente pose des problèmes de qualité (est "corrompue") et entend offrir une protection des problèmes mentionnés.
Vous pouvez penser de cette façon: si vous pouviez justifier que le concepteur de bibliothèque exposerait mieux ses fonctionnalités avec, Reasonable
au lieu de Messy
, cela signifierait que vous travaillez sur la couche anticorruption, que vous faites leur travail, corrigez leurs erreurs de conception.
Contrairement à cela, Adapter et Facade ne font aucune hypothèse sur la qualité de la conception sous-jacente. Celles-ci pourraient être appliquées à une API bien conçue pour commencer, en l'adaptant simplement à vos besoins spécifiques.
En fait, il pourrait être encore plus productif de supposer que des modèles tels que Adapter et Facade s’attendent à ce que le code sous-jacent soit bien conçu. Vous pouvez penser de cette façon: un code bien conçu ne devrait pas être trop difficile à peaufiner pour un cas d'utilisation particulier. S'il s'avère que la conception de votre adaptateur demande plus d'efforts que prévu, cela pourrait indiquer que le code sous-jacent est, d'une certaine manière, "corrompu". Dans ce cas, vous pouvez envisager de scinder le travail en phases distinctes: tout d'abord, créez une couche anticorruption pour présenter l'API sous-jacente d'une manière correctement structurée, puis concevez votre adaptateur / façade sur cette couche de protection.