Andriy Shyrokoryadov

.Net developer, data scientist

Фасад. Шаблоны проектирования - видео №7. №53.

Текст к видео "#53 Фасад" на канале YouTube

Приветствую Вас на моем канале. Помните, когда мы знакомились с первым принципом SOLID, принципом единичной ответственности, я сказал, что у это этого принципа есть один недостаток, а именно появление большого количества малых классов, которые выполняют какую-то определенную функцию. Если мы попытаемся связать принцип единичной ответственности и уже знакомый нам подход внедрения зависимостей, то мы можем получить следующую ситуацию: у нас могут появиться классы, которые в своём конструкторе могут принимать большое число зависимостей. Например, 1 – 3 параметра конструктора класса это нормально, но если нам необходимо 6-7, а может даже и больше, зависимостей, чтобы создать экземпляр одного класса, то такой код начинает быть проблематичным.

С другой стороны, такая ситуация может быть оправдана. То есть для работы конкретного класса необходимы все эти зависимости. Каждая из зависимостей выполняет свою единственную роль и делает это хорошо. Поэтому если мы видим, что конструктор класса принимает слишком много зависимостей – это не всегда означает плохой код. Наоборот, все может быть достаточно хорошо продумано, просто с таким классом тяжелее работать по сравнению с классом, который принимает меньшее число зависимостей.

Давайте рассмотрим пример и попытаемся разобраться как мы можем сделать нашу работу с таким кодом более комфортной, несмотря на большое число зависимостей.

В качестве примера я подготовил консольное приложение, в котором используется 3 зависимости. Наше приложение занимается отправкой заказов после оплаты в интернет-магазине. Под отправкой заказов подразумевается получение товара со склада, его оплата и последующая отправка курьерской службой. Также наше приложение может выполнить обратный процесс: вернуть посылку в магазин, вернуть оплату покупателю и в конце вернуть товар на склад. В итоге мы имеем 3 зависимости: интерфейс для доступа к услуге платежей, интерфейс для доступа к службе доставке, интерфейс для доступа к услуге склада. Как реализованы данные интерфейсы я не буду показывать, потому что для нашего примера это не особо важно, а если вам интересно вы можете скачать этот пример со страницы github и самостоятельно с ним ознакомиться.

Интерфейс для доступа к услуге платежей содержит члены для получения всех возможных способов платежей и определения величины комиссий, а также члены для осуществления платежа и его возврата.

Интерфейс для доступа к службе доставки содержит члены для получения всех возможных способов доставки и определения её стоимости, а также члены для отправки или возврата посылки.

Интерфейс для доступа к услуге к складу это такой мини шаблон репозиторий который содержит только 3 метода для получения и возврата товара с или на склад, а также метод для проверки есть ли товар на складе.

Так как мы уже знаем, как использовать подход внедрения зависимостей, в этом примере я использовал фреймфорк Autofac для регистрации и получения всех зависимостей вовремя работы приложения. Регистрация зависимостей происходит в методе RegisterDependencies и он вызывается в начале работы приложения. Далее зависимости будут получены из контейнера в случае необходимости в конкретных методах. Таких методов у нас два: Order и CancelOrder. Давайте ознакомимся с их имплементацией.

В методе Order в начале мы получаем все необходимые зависимости для оформления заказа. Далее мы проверяем существует ли заказанный товар на сладе и берём его со склада. В данном примере логика максимально упрощена чтобы упростить понимание примера, поэтому подразумевается, что независимо от проверки наличия товара на складе, товар всегда есть. Далее мы получаем все доступные методы отправки посылки и получаем стоимость одного из методов отправки. Третьим этапом является получение всех методов платежа и получение комиссии для одного из методов оплаты. После получения размера комиссии необходимо выполнить оплату. После успешно оплаты мы отправляем товар. Как видите в рамках оформления заказа мы выполняем достаточно большое количество операции при помощи 3 зависимостей.

С другой стороны, метод CancelOrder представляет из себя абсолютную противоположность метода Order. Если мы решили отменить заказ, то после получения всех необходимых зависимостей мы должны выполнить следующие действия. Получить все виды посылок и по типу отменить отправку заказа. Далее мы получаем все виды платежей и комиссий, после чего выполняем возврат средств клиенту. В конечном счете товар возвращается на склад. Операций чуть меньше, но всё равно их количество достаточно, чтобы запутаться.

Наш пример последовательно вызывает методы Order и CancelOrder. Запустим его и посмотрим на результат его работы. Как мы видим ничего необычного не произошло. Товар сначала был отправлен клиенту, но потом заказ был возвращен.

Как мы могли бы упростить этот код. Всё сложную логику, связанную с отправкой и возвратом заказа, мы могли бы скрыть за так называемым фасадом. Фасад – это интерфейс, как правило очень простой, реализация которого скрывает сложные внутренние механизмы бизнес-логики. В нашем пример я подготовил такой интерфейс. Он называется IOrderFacade. Данный интерфейс содержит всего лишь 2 метода Order и CancelOrder, которые не возвращают значений и не принимают никакие аргументы. Согласитесь, реализовать данный интерфейс очень просто. Давайте посмотрим на реализацию данного интерфейса, которая называется DefaultOrderFacade. Здесь логика очень напоминает то, что мы уже видели в классе Programm. Однако сам интерфейс IOrderFacade остается простым. Наблюдательные зрители, возможно, уже заметили, что класс DefaultOrderFacade реализует еще один интерфейс – IOrder. Это простой интерфейс для доступа к свойствам класса DefaultOrderFacade. Эти свойства являются параметрами методов, определенных в зависимостях. Таким образом мы могли реализовали передачу аргументов в методы зависимостей.

Давайте запустим пример с использованием интерфейса IOrderFacade и его реализации в виде DefaultOrderFacade. Как видите работа данного варианта кода ничем не отличается от предыдущего. Однако добавляя новый слой абстракции в виде интерфейса IOrderFacade мы позволили программистам, которые будут работать с данным кодом после нас, реализовывать логику отправки и возврата заказов без привязки к интерфейсам платежей, доставки и склада. Возможно, они захотят выполнить данную логику как то иначе. Наличие простого интерфейса IOrderFacade позволит выполнить эту задачу с малыми трудозатратами. С другой стороны код, который использует реализацию интерфейса IOrderFacade выглядит проще и более понятен при первом знакомстве с ними.

В начале данного видео я привел пример ситуации, когда код становится громоздким от большого количества зависимостей некоторых классов. Для того, чтобы уменьшить количество таких зависимостей, возможно стоит рассмотреть возможность объединения некоторых зависимостей в классах – фасадах и сокрытия нескольких зависимостей за общим интерфейсом – фасадом. Таким образом код станет более читабельным и понятным для других программистов, а может быть и для нас самих в будущем.

По вопросу шаблона проектирования «Фасад» на данный момент у меня всё. Я благодарю Вас за внимание и просмотр. Если данное видео показалось Вам хоть чуточку полезным я буду благодарен Вам за лайк и подписку. Положительный комментарий под видео также был бы кстати. Спасибо за внимание.

Пример кода из видео на GitHub

Для открытия файла проекта необходимо Visual Studio 2019.