Фасад
Что это такое?
Фасад - это структурный паттерн проектирования, который предоставляет простой интерфейс к более сложной системе классов.
Когда использовать?
- Когда имеется сложный модуль (или несколько модулей) в системе,и необходимо упростить с ним работу - фасад позволяет определить единую точку взаимодействия между клиентом и модулем.
- Когда необходимо уменьшить кол-во зависимостей между клиентом и сложным модулем - фасад "прячет" все зависимости внутри, оставляя только простой интерфейс взаимодействия
Концепт
На диаграмме UML фасад будет выглядеть примерно следующим образом:

Здесь модули - это различные классы, с которыми должна взаимодействовать наша система. Там может быть любое кол-во операций, полей. Класс Facade содержит в себе эти модули - они связаны отношением ассоциации. Модули можно инициализировать как где-то вовне фасада - тогда это будет агрегация, или внутри - композиция. Если вы ничего не поняли, советую вам прочитать статью "Отношения между классами и объектами" (Vk || Telegram).
В коде это будет выглядеть следующим образом. Для начала определим модули - они могут быть как написаны нами, так и находиться в какой-то внешней библиотеке
//какие-то внутренние модули
public class Module1
{
public void OperationA()
{
Console.WriteLine("operation a");
}
}
public class Module2
{
public void OperationB()
{
Console.WriteLine("operation b");
}
}
public class Module3
{
public void OperationC()
{
Console.WriteLine("operation c");
}
}
Далее опишем фасад. Он может быть как в простом варианте, то есть предоставлять доступ к классам модулей напрямую через свойства:
public class SimpleFacade
{
// модули, которые скрывает в себе фасад
public Module1 Module1 { get; }
public Module2 Module2 { get; }
public Module3 Module3 { get; }
public SimpleFacade()
{
// инициализация модулей
Module1 = new Module1();
Module2 = new Module2();
Module3 = new Module3();
}
}
так и в более строгом варианте, то есть содержать в себе готовые операции, использующие модуль определённым способом:
public class Facade
{
// модули, которые скрывает в себе фасад
private Module1 _module1;
private Module2 _module2;
private Module3 _module3;
public Facade()
{
// инициализация модулей
_module1 = new Module1();
_module2 = new Module2();
_module3 = new Module3();
}
// первая операция, которая вызывает в нужном порядке внутренние методы
public void FacadeOperationA()
{
_module1.OperationA();
_module3.OperationC();
}
// вторая операция, которая вызывает в нужном порядке внутренние методы
public void FacadeOperationB()
{
_module3.OperationC();
_module2.OperationB();
_module1.OperationA();
}
}
Используем наш фасад в классе Program:
class Program
{
static void Main(string[] args)
{
var facade = new Example.Facade();
Console.WriteLine();
facade.FacadeOperationA();
Console.WriteLine();
facade.FacadeOperationB();
}
}
Вывод:

Пример
Допустим, мы пишем интернет-магазин, где можно выбрать набор товаров, заказать их с доставкой и оплатить онлайн. Также возможен самовывоз и оплата наличными. При заказе клиент передаёт список товаров, адрес, тип оплаты и данные своей карты, а системе нужно сделать следующее:
- Получить массив выбранных товаров и их общую сумму
- Проверить товары на наличие на складе
- Для оплаты товаров воспользоваться сервисом платежей
- Создать заявку на доставку товаров
- Отправить клиенту уведомление на email об успешном заказе
При оплате офлайн вместо использования платёжного сервиса генерируется дополнительный счёт, который также отправляется на email, то есть порядок действий такой:
- Получить массив выбранных товаров и их общую сумму
- Проверить товары на наличие на складе
- Сгенерировать счёт
- Создать заявку на доставку товаров
- Отправить клиенту счёт
- Отправить клиенту уведомление на email об успешном заказе
Проектирование
В этот раз кода будет немного больше, чем обычно, поэтому подробнее остановимся и на проектировании. В первую очередь нам надо описать нашего клиента:

И продукт:

Затем опишем необходимые сервисы, или модули. Это классы, которые содержат в себе некий функционал и используются при заказе:

И сам фасад:

Полностью UML-диаграмма выглядит следующим образом:

Код
Определим модели продукта и клиента:
public class Product
{
public string Name { get; set; }
public int Cost { get; set; }
}
public class Client
{
public string Email { get; set; }
public string CardNumber { get; set; }
public DateTime CardDate { get; set; }
public int Cvc { get; set; }
public string Address { get; set; }
}
Далее определим сервисы. В реальных проектах они могут быть более сложны, для простоты воспользуемся "заглушками".
Сервис склада для проверки наличия продуктов:
public class WarehouseService
{
public bool CheckProducts(Product[] products)
{
// тут должна быть логика проверки товаров на складе
Console.WriteLine("Все продукты в наличии");
return true;
}
}
Платёжный сервис:
public class PaymentService
{
public void ExecutePayment(string number, DateTime date, int cvc, int cost)
{
// логика взаимодействия с платёжным сервисом
Console.WriteLine($"Выполнен платёж на сумму {cost} руб.");
}
public void CreateCheque(int cost)
{
// логика генерации чека на офлайн оплату
Console.WriteLine($"Чек для заказа на сумму {cost} руб.");
}
}
Сервис доставки:
public class DeliveryService
{
public void CreateDelivery(string address, Product[] products)
{
// логика создания заявки на доставку выбранных продуктов
Console.WriteLine("Заявка на доставку успешно зарегистрирована");
}
}
Сервис email:
public class EmailService
{
public void SendEmail(string email, string text)
{
//логика отправки письма
Console.WriteLine($"Сообщение отправлено на почту {email}");
Console.WriteLine(text);
}
}
Теперь нужно написать фасад:
public class OrderFacade
{
// набор сервисов, которые надо "обернуть в фасад"
private WarehouseService _warehouse;
private PaymentService _payment;
private DeliveryService _delivery;
private EmailService _email;
public OrderFacade()
{
// инициализируем зависимости в конструкторе класса
_warehouse = new WarehouseService();
_payment = new PaymentService();
_delivery = new DeliveryService();
_email = new EmailService();
}
public void CreateOfflineOrder(Client client, Product[] products)
{
// проверяем наличие товаров, и если их нет завершаем обработку заказа
if (!_warehouse.CheckProducts(products))
{
Console.WriteLine("Товаров нет на складе");
return;
}
// получаем общую цену всех продуктов
var cost = products.Sum(x => x.Cost);
// генерируем счёт
_payment.CreateCheque(cost);
// создаём доставку
_delivery.CreateDelivery(client.Address, products);
_email.SendEmail(client.Email, $"Ваш счёт: {cost} руб.");
_email.SendEmail(client.Email, "Ваш заказ создан");
}
public void CreateOnlineOrder(Client client, Product[] products)
{
// проверяем наличие товаров, и если их нет завершаем обработку заказа
if (!_warehouse.CheckProducts(products))
{
Console.WriteLine("Товаров нет на складе");
return;
}
// получаем общую цену всех продуктов
var cost = products.Sum(x => x.Cost);
// выполняем платёж
_payment.ExecutePayment(client.CardNumber, client.CardDate, client.Cvc, cost);
// создаём доставку
_delivery.CreateDelivery(client.Address, products);
_email.SendEmail(client.Email, "Ваш заказ создан");
}
}
Воспользуемся им в классе Program:
static void Main(string[] args)
{
var facade = new OrderFacade();
var client = new Client
{
Email = "a@a.a",
Cvc = 124,
Address = "ул. пушкина дом колотушкина",
CardDate = DateTime.Now,
CardNumber = "1234 1234 1234 1234"
};
var products = new[]
{
new Product {Name = "Продукт", Cost = 1000}
};
facade.CreateOnlineOrder(client, products);
}
Вывод приложения:

Делаем выводы
Фасад, по своей сути, является обёрткой для большого кол-ва модулей или классов. В реальных проектах он применяется достаточно часто. Его целью является скрытие большого кол-ва зависимостей и предоставление удобного контракта взаимодействия для других частей системы.
______________
С вами был Flex Code! Исходный код можно посмотреть тут