Wyobraź sobie, że musisz użyć kodu innej osoby, który został zaprojektowany w sposób pokazany poniżej:
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) { /* ... */ }
}
Teraz wyobraź sobie, że dowiadujesz się, że Twój zależny od niego kod wygląda następująco:
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?
}
... i chcesz ułatwić korzystanie, w szczególności, aby pozbyć się powtarzalnego używania parametrów, które po prostu nie są potrzebne w Twojej aplikacji.
Okej, więc zacznij budować warstwę antykorupcyjną.
Po pierwsze, upewnij się, że twój „główny kod” nie odnosi się Messy
bezpośrednio. Na przykład organizujesz zarządzanie zależnościami w taki sposób, aby próba uzyskania dostępu Messy
nie powiodła się.
Po drugie, tworzysz dedykowany moduł „warstwy”, który jako jedyny uzyskuje dostęp Messy
i udostępniasz go swojemu „głównemu kodowi” w sposób, który jest dla ciebie bardziej sensowny.
Kod warstwy wyglądałby następująco:
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); }
}
W rezultacie Twój „główny kod” nie zadziera z Messy
, używając Reasonable
zamiast tego około:
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");
}
Zauważ, że wciąż jest trochę bałaganu z Messy
tym bałaganem, ale teraz jest on ukryty dość głęboko w środku Reasonable
, dzięki czemu Twój „główny kod” jest dość czysty i wolny od uszkodzeń, które zostałyby wprowadzone przez bezpośrednie użycie Messy
rzeczy.
Powyższy przykład oparty jest na wyjaśnieniu warstwy antykorupcyjnej na wiki wiki c2:
Jeśli twoja aplikacja musi obsługiwać bazę danych lub inną aplikację, której model jest niepożądany lub nie ma zastosowania do modelu, który chcesz w swojej własnej aplikacji, skorzystaj z AnticorruptionLayer, aby przetłumaczyć na / z tego modelu i twojego.
Uwaga: przykład został celowo uproszczony i skrócony, aby wyjaśnienie było krótkie.
Jeśli masz większy bałagan interfejsu API, który można pokryć za warstwą antykorupcyjną, obowiązuje to samo podejście: po pierwsze, upewnij się, że „główny kod” nie ma bezpośredniego dostępu do uszkodzonych rzeczy, a po drugie, ujawnij go w sposób bardziej wygodne w kontekście użytkowania.
Kiedy „skalujesz” warstwę poza uproszczony przykład powyżej, weź pod uwagę, że uczynienie twojego API wygodnym niekoniecznie jest trywialnym zadaniem. Zainwestować wysiłek, aby zaprojektować warstwę właściwą drogę , zweryfikować jego przeznaczeniem z testów jednostkowych itp
Innymi słowy, upewnij się, że Twój interfejs API jest rzeczywiście lepszy od tego, który ukrywa, i upewnij się, że nie wprowadzasz kolejnej warstwy korupcji.
Dla kompletności zauważ subtelną, ale ważną różnicę między tym a pokrewnymi wzorami Adapter i Fasada . Jak wskazuje jego nazwa, warstwa antykorupcyjna zakłada, że u podstaw interfejsu API występują problemy z jakością (jest „uszkodzona”) i zamierza oferować ochronę wspomnianych problemów.
Można myśleć o tym w ten sposób: jeśli można uzasadnić, że projektant biblioteki byłoby lepiej odsłaniając jego funkcjonalność Reasonable
, a nie Messy
oznaczałoby to, że pracujesz na warstwie antykorupcyjnej, czyniąc ich pracę, ustalając swoje błędy projektowe.
W przeciwieństwie do tego, Adapter i Fasada nie przyjmują założeń dotyczących jakości projektu bazowego. Można je zastosować do interfejsu API, który jest dobrze zaprojektowany na początek, po prostu dostosowując go do konkretnych potrzeb.
W rzeczywistości bardziej produktywne byłoby założenie, że wzorce, takie jak Adapter i Fasada, oczekują, że kod bazowy będzie dobrze zaprojektowany. Możesz o tym pomyśleć w ten sposób: dobrze zaprojektowany kod nie powinien być zbyt trudny do dostosowania do konkretnego przypadku użycia. Jeśli okaże się, że projektowanie twojego adaptera wymaga więcej wysiłku niż się spodziewano, może to oznaczać, że kod źródłowy jest w jakiś sposób „uszkodzony”. W takim przypadku możesz rozważyć podzielenie zadania na oddzielne fazy: po pierwsze, utwórz warstwę antykorupcyjną, aby zaprezentować podstawowy interfejs API w odpowiednio ustrukturyzowany sposób, a następnie zaprojektuj adapter / fasadę na tej warstwie ochronnej.