Stellen Sie sich vor, Sie müssen den Code einer anderen Person verwenden, der wie folgt aufgebaut ist:
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) { /* ... */ }
}
Stellen Sie sich nun vor, Sie stellen fest, dass Ihr Code, der davon abhängt, wie folgt aussieht:
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?
}
... und dass Sie die Verwendung vereinfachen möchten, um insbesondere die wiederholte Verwendung von Parametern zu vermeiden, die für Ihre Anwendung nicht benötigt werden.
Okay, Sie beginnen mit dem Aufbau einer Antikorruptionsschicht.
Stellen Sie als Erstes sicher, dass Ihr "Hauptcode" nicht Messy
direkt darauf verweist . Beispielsweise ordnen Sie das Abhängigkeitsmanagement so an, dass der Zugriff Messy
nicht kompiliert werden kann.
Zweitens erstellen Sie ein dediziertes "Ebenen" -Modul, auf das nur zugegriffen wird, Messy
und setzen es Ihrem "Hauptcode" auf eine Weise aus, die für Sie sinnvoller ist.
Der Ebenencode würde folgendermaßen aussehen:
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); }
}
Als Ergebnis sieht Ihr „Hauptcode“ nicht mit Zohan an Messy
, indem Reasonable
stattdessen etwa wie folgt:
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");
}
Beachten Sie, dass es immer noch ein bisschen Chaos gibt, Messy
aber dies ist jetzt ziemlich tief im Inneren verborgen Reasonable
, was Ihren "Hauptcode" einigermaßen sauber und frei von Korruption macht, die durch die direkte Verwendung von Messy
Dingen dorthin gebracht würde .
Das obige Beispiel basiert auf der Erklärung der Antikorruptionsschicht im c2-Wiki:
Wenn Ihre Anwendung mit einer Datenbank oder einer anderen Anwendung arbeiten muss, deren Modell für das von Ihnen gewünschte Modell in Ihrer eigenen Anwendung unerwünscht oder nicht anwendbar ist, verwenden Sie eine Antikorruptions-Schicht, um in dieses und deins Modell zu übersetzen.
Hinweis Das Beispiel wurde absichtlich vereinfacht und komprimiert, um die Erklärung kurz zu halten.
Wenn Sie hinter der Anti-Korruptions-Ebene ein größeres Durcheinander an APIs haben, gilt der gleiche Ansatz: Stellen Sie zunächst sicher, dass Ihr "Hauptcode" nicht direkt auf beschädigte Inhalte zugreift, und legen Sie ihn dann auf eine umfassendere Weise offen bequem in Ihrem Nutzungskontext.
Berücksichtigen Sie beim "Skalieren" Ihrer Ebene über ein vereinfachtes Beispiel hinaus, dass es nicht unbedingt eine triviale Aufgabe ist, die API komfortabel zu gestalten. Investieren Sie eine Anstrengung , um Ihre Schicht die richtige Art und Weise zu entwerfen , überprüft die beabsichtigte Verwendung mit Unit - Tests usw.
Mit anderen Worten, stellen Sie sicher, dass Ihre API tatsächlich eine Verbesserung gegenüber der verborgenen API darstellt , und stellen Sie sicher, dass Sie nicht nur eine weitere Ebene der Korruption einführen.
Beachten Sie der Vollständigkeit halber subtile, aber wichtige Unterschiede zwischen diesem und verwandten Mustern Adapter und Fassade . Wie durch den Namen angegeben, geht die Antikorruptionsschicht davon aus, dass die zugrunde liegende API Qualitätsprobleme aufweist ("beschädigt" ist) und beabsichtigt, einen Schutz für die genannten Probleme anzubieten.
Sie können es auf diese Art und Weise denken: Wenn Sie diese Bibliothek Designer wäre besser rechtfertigen kann seine Funktionalität mit off Belichtung Reasonable
statt Messy
, würde dies bedeuten , Sie auf Anti - Korruptions - Ebene arbeiten, tun ihre Arbeit, Festsetzung ihre Design - Fehler.
Im Gegensatz dazu treffen Adapter und Facade keine Annahmen über die Qualität des zugrunde liegenden Designs. Diese können auf APIs angewendet werden, die für den Anfang gut konzipiert sind und nur an Ihre spezifischen Anforderungen angepasst werden.
Tatsächlich könnte es sogar produktiver sein anzunehmen, dass Muster wie Adapter und Facade voraussetzen, dass der zugrunde liegende Code gut gestaltet ist. Sie können sich das so vorstellen: Gut gestalteter Code sollte für bestimmte Anwendungsfälle nicht zu schwierig zu optimieren sein. Wenn sich herausstellt, dass das Design Ihres Adapters aufwändiger ist als erwartet, kann dies darauf hinweisen, dass der zugrunde liegende Code irgendwie "beschädigt" ist. In diesem Fall können Sie den Auftrag in verschiedene Phasen aufteilen: Richten Sie zunächst eine Antikorruptionsschicht ein, um die zugrunde liegende API ordnungsgemäß zu strukturieren, und entwerfen Sie anschließend Ihren Adapter / Ihre Fassade über dieser Schutzschicht.