Паттерн проектирования Фасад (Facade) на PHP. Паттерны проектирования: Фасад (Facade) Аналогия из жизни

Описание Remote Facade

Предоставляет общий объединяющий интерфейс для набора методов объекта для улучшения эффективности сетевого взаимодействия.

Паттерн Remote Facade в объектно-ориентированной модели улучшает работу с маленькими объектами, у которых маленькие методы. Маленькие методы открывают большие возможности для контроля и изменения поведения, а также для улучшения понимания клиентом работы приложения. Одно из последствий такого "мелко-молотого" поведения в том, что обычно происходит много взаимодействий между объектами с вызовом множества методов.

В одном адресном пространстве "мелко-молотые" взаимодействия работаю хорошо, но всё меняется, когда происходит взаимодействие между процессами. Удалённые вызовы более затратны, потому что многое надо сделать: иногда данные нужно упорядочить, проверить на безопасность, пакеты должны быть маршрутизированы на свичах. Если два процесса работают на разных краях света, даже скорость света может играть роль. Тяжелая правда в том, что любые межпроцессные взаимодействия на порядок более расточительны, чем анутрипроцессные вызовы. Даже, если оба процесса работают на одной машине. Такое влияние на производительность не может быть упущено из вида, даже приверженцами ленивой оптимизации.

В результате любой объект, который задействован в удалённом взаимодействии, нуждается в более общем интерфейсе, который бы позволил минимизировать количество запросов, необходимых, чтобы сделать что-либо. Это затрагивает не только методы, но и объекты. Вместо того, чтобы запрашивать счёт и все его пункты отдельно, надо считать и обновить все пункты счёта за одно обращение. Это влияет на всю структуру объекта. Надо забыть о благой цели малых объектов и малых методов. Программирование становится всё сложнее, продуктивность падает и падает.

Паттерн Remote Facade представляет собой общий "Фасад" (по GoF) поверх структуры более "мелко-молотых" объектов. Ни один из этих объектов не имеет удалённого интерфейса, а Remote Facade не включает в себя никакой бизнес-логики. Всё, что делает Remote Facade - это транслирует общие запросы в набор небольших запросов к подчинённым объектам.

11 марта 2010 в 10:30

Паттерн проектирования «Фасад» / «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;
}


{
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();
}
}

// Авторизация пользователя
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 : У меня у одного хабраредактор не работает?

Назначение паттерна Facade

  • Паттерн Facade предоставляет унифицированный интерфейс вместо набора интерфейсов некоторой подсистемы. Facade определяет интерфейс более высокого уровня, упрощающий использование подсистемы.
  • Паттерн Facade "обертывает" сложную подсистему более простым интерфейсом.

Решаемая проблема

Клиенты хотят получить упрощенный интерфейс к общей функциональности сложной подсистемы.

Обсуждение паттерна Facade

Паттерн Facade инкапсулирует сложную подсистему в единственный интерфейсный объект. Это позволяет сократить время изучения подсистемы, а также способствует уменьшению степени связанности между подсистемой и потенциально большим количеством клиентов. С другой стороны, если фасад является единственной точкой доступа к подсистеме, то он будет ограничивать возможности, которые могут понадобиться "продвинутым" пользователям.

Объект Facade, реализующий функции посредника, должен оставаться довольно простым и не быть всезнающим "оракулом".

Структура паттерна Facade

Клиенты общаются с подсистемой через Facade. При получении запроса от клиента объект Facade переадресует его нужному компоненту подсистемы. Для клиентов компоненты подсистемы остаются "тайной, покрытой мраком".

Подсистемы SubsystemOne и SubsystemThree не взаимодействуют напрямую с внутренними компонентами подсистемы SubsystemTwo. Они используют "фасад" SubsystemTwoWrapper (т.е. абстракцию более высокого уровня).

Паттерн Facade определяет унифицированный высокоуровневый интерфейс к подсистеме, что упрощает ее использование. Покупатели сталкиваются с фасадом при заказе каталожных товаров по телефону. Покупатель звонит в службу поддержки клиентов и перечисляет товары, которые хочет приобрести. Представитель службы выступает в качестве "фасада", обеспечивая интерфейс к отделу исполнения заказов, отделу продаж и службе доставки.

  • Определите для подсистемы простой, унифицированный интерфейс.
  • Спроектируйте класс "обертку", инкапсулирующий подсистему.
  • Вся сложность подсистемы и взаимодействие ее компонентов скрыты от клиентов. "Фасад" / "обертка" переадресует пользовательские запросы подходящим методам подсистемы.
  • Клиент использует только "фасад".
  • Рассмотрите вопрос о целесообразности создания дополнительных "фасадов".

Особенности паттерна Facade

  • Facade определяет новый интерфейс, в то время как Adapter использует уже имеющийся. Помните, Adapter делает работающими вместе два существующих интерфейса, не создавая новых.
  • Если Flyweight показывает, как сделать множество небольших объектов, то Facade показывает, как сделать один объект, представляющий целую подсистему.
  • Mediator похож на Facade тем, что абстрагирует функциональность существующих классов. Однако Mediator централизует функциональность между объектами-коллегами, не присущую ни одному из них. Коллеги обмениваются информацией друг с другом через Mediator. С другой стороны, Facade определяет простой интерфейс к подсистеме, не добавляет новой функциональности и не известен классам подсистемы.
  • Abstract Factory может применяться как альтернатива Facade для сокрытия платформенно-зависимых классов.
  • Объекты "фасадов" часто являются Singleton , потому что требуется только один объект Facade.
  • Adapter и Facade в являются "обертками", однако эти "обертки" разных типов. Цель Facade - создание более простого интерфейса, цель Adapter - адаптация существующего интерфейса. Facade обычно "обертывает" несколько объектов, Adapter "обертывает" один объект.

Реализация паттерна Facade

Разбиение системы на компоненты позволяет снизить ее сложность. Ослабить связи между компонентами системы можно с помощью паттерна Facade. Объект "фасад" предоставляет единый упрощенный интерфейс к компонентам системы.

В примере ниже моделируется система сетевого обслуживания. Фасад FacilitiesFacade скрывает внутреннюю структуру системы. Пользователь, сделав однажды запрос на обслуживание, затем 1-2 раза в неделю в течение 5 месяцев справляется о ходе выполнения работ до тех пор, пока его запрос не будет полностью обслужен.

#include class MisDepartment { public: void submitNetworkRequest() { _state = 0; } bool checkOnStatus() { _state++; if (_state == Complete) return 1; return 0; } private: enum States { Received, DenyAllKnowledge, ReferClientToFacilities, FacilitiesHasNotSentPaperwork, ElectricianIsNotDone, ElectricianDidItWrong, DispatchTechnician, SignedOff, DoesNotWork, FixElectriciansWiring, Complete }; int _state; }; class ElectricianUnion { public: void submitNetworkRequest() { _state = 0; } bool checkOnStatus() { _state++; if (_state == Complete) return 1; return 0; } private: enum States { Received, RejectTheForm, SizeTheJob, SmokeAndJokeBreak, WaitForAuthorization, DoTheWrongJob, BlameTheEngineer, WaitToPunchOut, DoHalfAJob, ComplainToEngineer, GetClarification, CompleteTheJob, TurnInThePaperwork, Complete }; int _state; }; class FacilitiesDepartment { public: void submitNetworkRequest() { _state = 0; } bool checkOnStatus() { _state++; if (_state == Complete) return 1; return 0; } private: enum States { Received, AssignToEngineer, EngineerResearches, RequestIsNotPossible, EngineerLeavesCompany, AssignToNewEngineer, NewEngineerResearches, ReassignEngineer,EngineerReturns, EngineerResearchesAgain, EngineerFillsOutPaperWork, Complete }; int _state; }; class FacilitiesFacade { public: FacilitiesFacade() { _count = 0; } void submitNetworkRequest() { _state = 0; } bool checkOnStatus() { _count++; /* Запрос на обслуживание получен */ if (_state == Received) { _state++; /* Перенаправим запрос инженеру */ _engineer.submitNetworkRequest(); cout << "submitted to Facilities - " << _count << " phone calls so far" << endl; } else if (_state == SubmitToEngineer) { /* Если инженер свою работу выполнил, перенаправим запрос электрику */ if (_engineer.checkOnStatus()) { _state++; _electrician.submitNetworkRequest(); cout << "submitted to Electrician - " << _count << " phone calls so far" << endl; } } else if (_state == SubmitToElectrician) { /* Если электрик свою работу выполнил, перенаправим запрос технику */ if (_electrician.checkOnStatus()) { _state++; _technician.submitNetworkRequest(); cout << "submitted to MIS - " << _count << " phone calls so far" << endl; } } else if (_state == SubmitToTechnician) { /* Если техник свою работу выполнил, то запрос обслужен до конца */ if (_technician.checkOnStatus()) return 1; } /* Запрос еще не обслужен до конца */ return 0; } int getNumberOfCalls() { return _count; } private: enum States { Received, SubmitToEngineer, SubmitToElectrician, SubmitToTechnician }; int _state; int _count; FacilitiesDepartment _engineer; ElectricianUnion _electrician; MisDepartment _technician; }; int main() { FacilitiesFacade facilities; facilities.submitNetworkRequest(); /* Звоним, пока работа не выполнена полностью */ while (!facilities.checkOnStatus()) ; cout << "job completed after only " << facilities.getNumberOfCalls() << " phone calls" << endl; }

Вывод программы:

submitted to Facilities - 1 phone calls so far submitted to Electrician - 12 phone calls so far submitted to MIS - 25 phone calls so far job completed after only 35 phone calls

Фасад - это структурный паттерн проектирования, который предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку.

Проблема

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

В результате бизнес-логика ваших классов тесно переплетается с деталями реализации сторонних классов. Такой код довольно сложно понимать и поддерживать.

Решение

Фасад - это простой интерфейс для работы со сложной подсистемой, содержащей множество классов. Фасад может иметь урезанный интерфейс, не имеющий 100% функциональности, которой можно достичь, используя сложную подсистему напрямую. Но он предоставляет именно те фичи, которые нужны клиенту, и скрывает все остальные.

Фасад полезен, если вы используете какую-то сложную библиотеку со множеством подвижных частей, но вам нужна только часть её возможностей.

К примеру, программа, заливающая видео котиков в социальные сети, может использовать профессиональную библиотеку сжатия видео. Но все, что нужно клиентскому коду этой программы - простой метод encode(filename, format) . Создав класс с таким методом, вы реализуете свой первый фасад.

Аналогия из жизни


Когда вы звоните в магазин и делаете заказ по телефону, сотрудник службы поддержки является вашим фасадом ко всем службам и отделам магазина. Он предоставляет вам упрощённый интерфейс к системе создания заказа, платёжной системе и отделу доставки.

Структура



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

    Дополнительный фасад можно ввести, чтобы не «захламлять» единственный фасад разнородной функциональностью. Он может использоваться как клиентом, так и другими фасадами.

    Сложная подсистема состоит из множества разнообразных классов. Для того, чтобы заставить их что-то делать, нужно знать подробности устройства подсистемы, порядок инициализации объектов и так далее.

    Классы подсистемы не знают о существовании фасада и работают друг с другом напрямую.

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

Псевдокод

В этом примере Фасад упрощает работу со сложным фреймворком видеоконвертации.


Пример изоляции множества зависимостей в одном фасаде.

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

// Классы сложного стороннего фреймворка конвертации видео. Мы // не контролируем этот код, поэтому не можем его упростить. class VideoFile // ... class OggCompressionCodec // ... class MPEG4CompressionCodec // ... class CodecFactory // ... class BitrateReader // ... class AudioMixer // ... // Вместо этого мы создаём Фасад - простой интерфейс для работы // со сложным фреймворком. Фасад не имеет всей функциональности // фреймворка, но зато скрывает его сложность от клиентов. class VideoConverter is method convert(filename, format):File is file = new VideoFile(filename) sourceCodec = new CodecFactory.extract(file) if (format == "mp4") destinationCodec = new MPEG4CompressionCodec() else destinationCodec = new OggCompressionCodec() buffer = BitrateReader.read(filename, sourceCodec) result = BitrateReader.convert(buffer, destinationCodec) result = (new AudioMixer()).fix(result) return new File(result) // Приложение не зависит от сложного фреймворка конвертации // видео. Кстати, если вы вдруг решите сменить фреймворк, вам // нужно будет переписать только класс фасада. class Application is method main() is convertor = new VideoConverter() mp4 = convertor.convert("funny-cats-video.ogg", "mp4") mp4.save()

Применимость

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

Часто подсистемы усложняются по мере развития программы. Применение большинства паттернов приводит к появлению меньших классов, но в бóльшем количестве. Такую подсистему проще повторно использовать, настраивая её каждый раз под конкретные нужды, но вместе с тем, применять подсистему без настройки становится труднее. Фасад предлагает определённый вид системы по умолчанию, устраивающий большинство клиентов.

Когда вы хотите разложить подсистему на отдельные слои.

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

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

Шаги реализации

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

    Создайте класс фасада, реализующий этот интерфейс. Он должен переадресовывать вызовы клиента нужным объектам подсистемы. Фасад должен будет позаботиться о том, чтобы правильно инициализировать объекты подсистемы.

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

Фасад (Facade ) — это поведенческий шаблон проектирования. Данный шаблон позволяет скрыть сложность системы путём сведения всех возможных вызовов к одному объекту, делегирующему их соответствующих объектам системы.

Простейшая схема работы паттерна:

Представим, что ми пишем программное обеспечение для микроволновой печи. Для простоты представим, что у неё есть только следующие функции: поворот влево и вправо, установка необходимой мощности, оповещение о начале и завершении работы.

Для того, чтобы приготовить вкусное блюдо, что-то разогреть или разморозить, необходимо выполнить определённое количество различных действий, в определённой последовательности. Например при разморозке, необходимо начиная с высоких мощностей несколько раз сбрасывать мощность, при этом вращая платформу с продуктом.

Если бы пользователю приходилось самому следить за каждым шагом процесса, то это было бы очень времязатратно и неэффективно. Ведь все знают, что на современной микроволновке достаточно выбрать нужную программу и нажать старт, после чего она сама сделает всё что необходимо, а по завершению оповестит пользователя.

Шаблон проектирования «фасад» занимается именно такими случаями. Он позволяет спрятать всю сложность процесса за простым интерфейсом.

Создадим классы для работы привода микроволновки (вращения), мощности и оповещения.

В классе привода, будет всего 2 действия: поворот направо и поворот налево.

Class Drive { public void TurlLeft() { Console.WriteLine("Повернуть влево"); } public void TurlRight() { Console.WriteLine("Повернуть вправо"); } }

В классе задающем мощность, добавлено свойство для получения и установки необходимой мощности работы.

Class Power { private int _power; public int MicrowavePower { get { return _power; } set { _power = value; Console.WriteLine("Задана мощность {0}w ", _power); } } }

В классе оповещения добавлены методы для оповещения о начале и завершении работы.

Class Notification { public void StopNotification() { Console.WriteLine("Пик-пик-пик - операция завершена"); } public void StartNotification() { Console.WriteLine("Пик - начат процесс готовки"); } }

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

Class Microwave { private Drive _drive; private Power _power; private Notification _notification; public Microwave(Drive drive, Power power, Notification notification) { _drive = drive; _power = power; _notification = notification; } public void Defrost() { _notification.StartNotification(); _power.MicrowavePower = 1000; _drive.TurlRight(); _drive.TurlRight(); _power.MicrowavePower = 500; _drive.TurlLeft(); _drive.TurlLeft(); _power.MicrowavePower = 200; _drive.TurlRight(); _drive.TurlRight(); _power.MicrowavePower = 0; _notification.StopNotification(); } public void Heating() { _notification.StartNotification(); _power.MicrowavePower = 350; _drive.TurlRight(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlLeft(); _drive.TurlLeft(); _drive.TurlRight(); _drive.TurlRight(); _drive.TurlLeft(); _drive.TurlLeft(); _drive.TurlLeft(); _drive.TurlLeft(); _power.MicrowavePower = 0; _notification.StopNotification(); } }

Вот и всё, пример готов, осталось протестировать.

Var drive = new Drive(); var power = new Power(); var notification = new Notification(); var microwave = new Microwave.Microwave(drive, power, notification); Console.WriteLine("Разморозим"); microwave.Defrost(); Console.WriteLine(); Console.WriteLine("Подогреем"); microwave.Heating();

Результат будет следующий:


Похожие статьи

© 2024 ap37.ru. Сад и огород. Декоративные кустарники. Болезни и вредители.