Pull to refresh

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

Reading time 5 min
Views 76K
Почитать описание других паттернов.

Проблема


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

Описание


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

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

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

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

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

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


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

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


Рассмотрим диаграмму. Каркас подсистемы авторизации, для наглядности, выделен в прямоугольник. Фасад 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: У меня у одного хабраредактор не работает?
Tags:
Hubs:
+27
Comments 21
Comments Comments 21

Articles