Паттерн проектирования «Фасад» / «Facade»

    Почитать описание других паттернов.

    Проблема


    Минимизировать зависимость подсистем некоторой сложной системы и обмен информацией между ними.

    Описание


    При проектировании сложных систем, зачастую применяется т.н. принцип декомпозиции, при котором сложная система разбивается на более мелкие и простые подсистемы. Причем, уровень декомпозиции (ее глубину) определяет исключительно проектировщик. Благодаря такому подходу, отдельные компоненты системы могу быть разработаны изолированно, затем интегрированы вместе. Однако возникает, очевидная на первый взгляд, проблема — высокая связность модулей системы. Это проявляется, в первую очередь, в большом объеме информации, которой модули обмениваются друг с другом. К тому же, для подобной коммуникации одни модули должны обладать достаточной информацией о природе других модулей.

    Таким образом, минимизация зависимости подсистем, а также снижение объема передаваемой между ними информации — одна из основных задач проектирования.

    Один из способов решения данной задачи — использование паттерна «Фасад».

    Паттерн «Фасад» предоставляет унифицированный интерфейс вместо набора интерфейсов некоторой подсистемы. Фасад определяет интерфейс более высокого уровня, кото-
    рый упрощает использование подсистемы.

    Проще говоря, «Фасад» — есть ни что иное, как некоторый объект, аккумулирующий в себе высокоуровневый набор операций для работы с некоторой сложной подсистемой. Фасад агрегирует классы, реализующие функциональность этой подсистемы, но не скрывает их. Важно понимать, что клиент, при этом, не лишается более низкоуровневого доступа к классам подсистемы, если ему это, конечно, необходимо. Фасад упрощает выполнение некоторых операций с подсистемой, но не навязывает клиенту свое использование.

    Практическая задача


    Используя паттерн «Фасад», реализуем унифицированный интерфейс к некоторой подсистеме авторизации пользователей. Сама подсистема авторизации (в данном примере), безусловно не претендует на «сложную систему», однако она отчетливо отображает основные достоинства паттерна.

    Диаграмма классов


    Рассмотрим диаграмму. Каркас подсистемы авторизации, для наглядности, выделен в прямоугольник. Фасад Authorizator предоставляет клиенту унифицированный интерфейс для работы с подсистемой. В данном случае, это всего один метод — authorizate(), однако их могло быть и больше. При этом, клиент может использовать фасад для работы с подсистемой, а может, непосредственно пользоваться классами, составляющими ее. Сам процесс авторизации достаточно прост. На основании имени пользователя ищется соответствующая запись в базе данных, посредством интерфейса DB. Затем, сравнивается пароль найденной записи с паролем указанным пользователем.


    Реализация на С#


    В коде реализации нет класса PgSQLDB.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Security;

    namespace Facade
    {
      //Абстрактный класс пользователя
      abstract class User
      {
        protected string username;
        protected string passwd;

        public abstract string getUserRole();

        public string getPasswdHash()
        {
          // Это строка не несет какой-либой смысловой нагрузки.
          // Безусловно, таким образом мы получаем небезопасный хеш-код пароля
          return passwd.GetHashCode().ToString();
        }
      }

      // Уточнение пользователя, в качестве пользователя по-умолчанию
      class DefaultUser : User
      {
        public DefaultUser(string username, string passwd)
        {
          this.username = username;
          this.passwd = passwd;
        }

        public override string getUserRole()
        {
          return "DEFAULT_USER";
        }
      }

      // Уточнение пользователя, в качестве администратора
      class Administrator : User
      {
        public Administrator(string username, string passwd)
        {
          this.username = username;
          this.passwd = passwd;
        }

        public override string getUserRole()
        {
          return "ADMINISTRATOR";
        }

      }

      // Интерфейс доступа к базе данных
      interface DB
      {
        User search(string username);
      }

      // Реализация интерфейса БД для SQLite
      class SQLiteDB : DB
      {
        public SQLiteDB(string filename)
        {
          // Инициализация драйвера БД
        }

       
        public User search(string username)
        {
          // Заглушка
          throw new NotImplementedException();
        }
      }

      // Фасад - авторизатор пользователей
      class Authorizator
      {
        public Authorizator()
        {
          // Инициализация авторизатора
        }

        // Авторизация пользователя
        public void authorizate(string username, string passwd)
        {
          DB db = new SQLiteDB("db.sqlite");
          User user = db.search(username);
          if (user.getPasswdHash() == passwd)
          {
            // все хорошо, пользователь опознан
          }
          else
          {
            // что-то пошло не так
            throw new SecurityException("Wrong password or username!");
          }
        }
      }
       
      class Program
      {
        static void Main(string[] args)
        {
          // Вымышленный пользователь
          string username = "Vasya";
          string passwd = "qwerty".GetHashCode().ToString();
          
          Authorizator auth = new Authorizator();
          try
          {
            auth.authorizate(username, passwd);
          }
          catch (SecurityException ex)
          {
            // Пользователь не прошел аутентификацию
          }
        }
      }
    }

    * This source code was highlighted with Source Code Highlighter.

    PS: У меня у одного хабраредактор не работает?
    Поделиться публикацией

    Комментарии 21

      +5
      вот теперь то я понял, что именно я реализовывал… делал, а названия не знал.

      спасибо за статьи. Просто, кратко, понятно.
        +3
        Пожалуйста :)
          0
          Как раз начал изучение паттернов =). Если я верно понял, то Фасад-это просто класс, объединяющий результаты работы некоторых объектов других классов? (наверно глупо поставленный вопрос, просто мне так проще=> лучше запомнится… И вообще кажется понимание паттернов-совсем не сложно, а я сейчас читаю «Банду 4-ех», так там все описано ОТЛИЧНО, но сложно =( Хотя и это, возможно, только для меня так и я не нашел верного подхода к изучению паттернов по ней… Читаю, разбираю, а они забываются в нужных ситуациях примениться). Спасибо за статьи.
          +1
          Это нормально. Паттерны — это не только готовые рецепты для написания нового кода, но и средство для описания и наведения порядка в имеющемся.
          0
          Я так понимаю, любой «продвинутый» контроллер может являться фасадом. Например, как раз он и может осуществлять авторизацию, помимо выполнения своих основных функций.

          Здесь, кстати, полезно провести параллели с шаблоном Builder — этот шаблон может не только «создавать», но и «настраивать» произвольное количество объектов, скрывая всю сложность от клиента.
            0
            >Я так понимаю, любой «продвинутый» контроллер может являться фасадом.

            Может но это не очень хороший вариант, т.к. как он применим только когда есть один вариант использования (web). А что, если вам еще веб-сервис нужно будет слепить? Как правило фассад это почти то же самое что и сервисный слой, по крайней мере я это так понимаю. А дальше этот фассад уже и дергается в контроллерах, веб-сервисах.
            +6
            На самом деле, многие из нас неявно используют те или иные паттерны. Но четкое понимание их устройства и применимости к ситуациям приносит больше пользы, чем их неявное использование. Плюс знание имен паттернов очень помогает при общении, когда тебя с полуслова понимают: «берем синглтон», «вот тут используй визитора», «а вот здесь нужен адаптер». Еще один момент — при намеренном использовании паттернов часто используются соответствующие имена или префиксы для классов, их реализующих, например, ConnectionFactory, XMLNodeVisitor — это позволяет легко распознавать паттерны при чтении кода.
              –4
              Синглтон — это уже антипаттерн. Просто к слову :)
                0
                С каких это пор?
                  +2
                  Да вот как-то так неожиданно случилось. Основной недостаток — сложность тестирования. Отличная замена синглтонам — тулкит. Тулкит, правда, сам является синглтоном, но единственным в проекте, а при правильной реализации (не монолитный класс, а набор тулзов) лишен всех недостатков обычного синглтона.
                    +1
                    Сложность тестирования можно победить, добавив метод setInstance. Главная его проблема — статическая зависимость
                      –1
                      Кстати, тулкит пару лет назад как раз в лимбе подсмотрел :) В документации к лимбу хорошее описание было и еще, вроде бы, в статье об инверсии зависимостей.
              0
              какая есть литература и источники конкретно по паттернам проектирования?
              +2
              Фасад, на мой взгляд, один из наиболее простых для понимания паттернов, и его действительно часто многие используют, даже не зная о нем.
                0
                То есть фасадом является, например, любой унифицированный интерфейс для плагинов навроде того же CallService миранды?
                  0
                  Можно ли поправить текст статьи? Текст программы отображается в виде HTML кода: https://habrastorage.org/files/c64/f30/61c/c64f3061ce2a4c59afaab14477fd9bc9.png

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое