Представьте, что вы должны использовать чужой код, который разработан, как показано ниже:
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) { /* ... */ }
}
Теперь представьте, что вы обнаружите, что ваш код, который зависит от него, выглядит следующим образом:
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?
}
... и что вы хотите упростить использование, в частности, избавиться от повторного использования параметров, которые просто не нужны для вашего приложения.
Итак, вы начинаете создавать антикоррупционный слой.
Прежде всего, убедитесь, что ваш «основной код» не ссылается Messy
напрямую. Например, вы организуете управление зависимостями таким образом, что попытка доступа Messy
не скомпилируется.
Во-вторых, вы создаете выделенный модуль «layer», который является единственным доступным, Messy
и представляете его своему «основному коду» таким образом, чтобы он был более понятным для вас.
Код слоя будет выглядеть следующим образом:
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); }
}
В результате ваш «основной код» не связывается Messy
, используя Reasonable
вместо этого примерно следующее:
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");
}
Заметьте, что все еще есть небольшая путаница, Messy
но теперь она достаточно глубоко спрятана Reasonable
, что делает ваш «основной код» достаточно чистым и свободным от повреждений, которые могут возникнуть при непосредственном использовании Messy
вещей.
Приведенный выше пример основан на том, как антикоррупционный слой объясняется в c2 wiki:
Если вашему приложению необходимо иметь дело с базой данных или другим приложением, модель которого нежелательна или неприменима к модели, которую вы хотите в своем собственном приложении, используйте AnticorruptionLayer для перевода в / из этой и вашей модели.
Пример примечания намеренно сделан простым и сжатым для краткости пояснения.
Если у вас есть большой беспорядок API, чтобы покрыть антикоррупционный уровень, применяется тот же подход: во-первых, убедитесь, что ваш «основной код» не имеет прямого доступа к поврежденным материалам, а во-вторых, представьте его таким образом, который более удобно в вашем контексте использования.
Когда «масштабируете» свой слой за пределы упрощенного примера, приведенного выше, учтите, что сделать ваш API удобным не обязательно тривиальная задача. Инвестируйте усилия, чтобы правильно спроектировать свой слой , проверьте его предполагаемое использование с помощью модульных тестов и т. Д.
Другими словами, убедитесь, что ваш API действительно является улучшением по сравнению с тем, который он скрывает, убедитесь, что вы не просто добавили еще один уровень коррупции.
Для полноты заметим тонкое, но важное различие между этим и связанными паттернами Adapter и Facade . Как видно из его названия, антикоррупционный уровень предполагает, что базовый API имеет проблемы с качеством («поврежден») и намеревается обеспечить защиту упомянутых проблем.
Вы можете думать об этом следующим образом: если вы можете доказать, что дизайнеру библиотеки было бы лучше раскрыть его функциональность Reasonable
вместо Messy
, это означало бы, что вы работаете над антикоррупционным слоем, выполняете их работу, исправляете их ошибки проектирования.
В отличие от этого, Adapter и Facade не делают предположений о качестве основного дизайна. Они могут быть применены к API, который хорошо разработан с самого начала, просто адаптируя его под ваши конкретные потребности.
На самом деле, было бы даже более продуктивно предположить, что такие шаблоны, как Adapter и Facade, ожидают, что базовый код будет хорошо спроектирован. Вы можете думать об этом так: хорошо разработанный код не должен быть слишком сложным для настройки для конкретного случая использования. Если окажется, что разработка вашего адаптера требует больше усилий, чем ожидалось, это может указывать на то, что лежащий в основе код, ну, как-то «поврежден». В этом случае вы можете рассмотреть возможность разделения работы на отдельные этапы: сначала создайте антикоррупционный слой для правильного представления базового API, а затем создайте свой адаптер / фасад поверх этого уровня защиты.