Представьте, что вы должны использовать чужой код, который разработан, как показано ниже:
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, а затем создайте свой адаптер / фасад поверх этого уровня защиты.
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.
Весь этот раздел достоин жирного тега.Процитирую другой источник:
Эрик Эванс, Дизайн, управляемый доменом, 16-я печать, страница 365
Самое главное, что на каждой стороне антикоррупционного уровня используются разные термины. Я когда-то работал над системой транспортной логистики. Туры должны были быть запланированы. Вы должны были оборудовать автомобиль в депо, доехать до различных мест обслуживания клиентов и обслуживать их, а также посетить другие места, например, остановку в баке. Но на более высоком уровне это было все о планировании задач. Поэтому имело смысл отделить более общие термины планирования задач от очень специфических логистических условий транспортировки.
Таким образом, изоляция антикоррупционных слоев заключается не только в защите вас от грязного кода, но и в том, чтобы отделить разные домены и обеспечить их разделение в будущем.
источник
адаптер
Когда у вас есть несовместимые интерфейсы, которые выполняют аналогичную логику, чтобы адаптировать один к другому, так что вы можете использовать реализации одного с вещами, которые ожидают другого.
Пример:
У вас есть объект, который хочет Car, но у вас есть только класс 4WheelVehicle, поэтому вы создаете CarBuiltUsing4WheelVehicle и используете его как свой автомобиль.
Фасад
Когда у вас сложный / запутанный / гигантский API, и вы хотите сделать его проще / понятнее / меньше. Вы создадите Фасад, чтобы скрыть сложность / путаницу / дополнительные возможности и только выставить новый простой / понятный / маленький API.
Пример:
Вы используете библиотеку, которая имеет 100 методов, и для выполнения определенной задачи вам нужно выполнить множество инициализаций, подключений, открытий / закрытий, просто чтобы наконец иметь возможность делать то, что вы хотели, и все, что вам нужно, - это 1 особенность все 50, которые может делать библиотека, поэтому вы создаете Фасад, в котором есть только метод для той функции, которая вам нужна, и которая выполняет всю инициализацию, очистку и т. д. за вас.
Антикоррупционный слой
Если у вас есть система, которая находится за пределами вашего домена, но ваши бизнес-потребности требуют, чтобы вы работали с этим другим доменом. Вы не хотите вводить этот другой домен в свой собственный, поэтому портите его, чтобы перенести концепцию своего домена в этот другой домен и наоборот.
Пример:
Одна система просматривает клиента, имеющего имя и список строк, по одной для каждой транзакции. Вы рассматриваете Профили как отдельные классы, имеющие имя, а Транзакции как отдельные классы со строкой, а Заказчик - как Профиль и коллекцию Транзакций.
Таким образом, вы создаете слой ACL, который позволит переводить между вашим клиентом и клиентом другой системы. Таким образом, вам никогда не придется использовать клиента другой системы, вам просто нужно сказать ACL: «дайте мне клиента с профилем X, а ACL скажет другой системе присвоить ему клиента с именем X.name и вернет его». Вы клиент с профилем X.
====================
Все три относительно похожи, потому что все они являются паттернами косвенности. Но они касаются разных структур, классов / объектов, API-интерфейсов и модулей / подсистем. Вы могли бы объединить их все, если вам нужно. Подсистема имеет сложный API, поэтому вы создаете для нее FACADE, она использует другую модель, поэтому для каждого представления данных, которое не соответствует вашей модели, вы должны перевести эти данные обратно в то, как вы их моделируете. Наконец, возможно, интерфейсы также несовместимы, поэтому вы должны использовать АДАПТЕРЫ для адаптации от одного к другому.
источник
Многие ответы здесь говорят о том, что списки ACL "не просто" для обертывания грязного кода. Я бы пошел дальше и сказал, что они вовсе не об этом, и если они это сделают, то это побочная выгода.
Антикоррупционный уровень предназначен для отображения одного домена на другой, так что службы, использующие второй домен, не должны быть «повреждены» концепциями первого. ACL для моделей предметной области - то же самое, что адаптеры для классов, это просто происходит на другом уровне. Адаптер, пожалуй, является наиболее важным шаблоном проектирования - я использую его все время - но судить обернутый класс как беспорядочный или нет, не имеет значения. Это то, что есть, мне просто нужен другой интерфейс.
Сосредоточение внимания на беспорядке вводит в заблуждение и упускает суть сути DDD. ACL предназначены для решения концептуальных несоответствий, а не низкого качества.
источник