Меню
Главная
Авторизация/Регистрация
 
Главная arrow Информатика arrow Введение в архитектуру программного обеспечения

Паттерн Фасад (Facade)

Название и классификация паттерна

Фасад — паттерн, структурирующий объекты.

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

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

Паттерн Facade «обертывает» сложную подсистему более простым интерфейсом.

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

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

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

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

Схематичное представление паттерна Фасад

Рис. 50. Схематичное представление паттерна Фасад

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

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

Рассмотрим, например, среду программирования, которая дает приложениям доступ к подсистеме компиляции. В этой подсистеме имеются такие классы, как Scanner (лексический анализатор), Parser (синтаксический анализатор), ProgramNode (узел программы), BytecodeStream (поток байтовых кодов) и ProgramNodeBuilder (строитель узла программы). Все вместе они составляют компилятор. Некоторым специализированным приложениям, возможно, понадобится прямой доступ к этим классам. Но для большинства клиентов компилятора такие детали, как синтаксический разбор и генерация кода, обычно не нужны; им просто требуется откомпилировать некоторую

Compiler

Классы

подсистемы

компиляции

CompileQ

Scanner

Token

Stream

----?

Parser

Symbol

—?

BytecodeStream

CodeGenerator

A

ProgramNodeBuilder

ProgramNode

StatementNode

StackMachineCodeGenerator

RISCCodeGenerator

A

ExpressionNode

VariableNode

Рис. 51. Схематичное представление компилятора

программу. Для таких клиентов применение мощного, но низкоуровневого интерфейса подсистемы компиляции только усложняет задачу (рис. 51).

Чтобы предоставить интерфейс более высокого уровня, изолирующий клиента от этих классов, в подсистему компиляции включен также класс Compiler (компилятор). Он определяет унифицированный интерфейс ко всем возможностям компилятора. Класс Compiler выступает в роли Фасада: предлагает простой интерфейс к более сложной подсистеме. Он «склеивает» классы, реализующие функциональность компилятора, но не скрывает их полностью. Благодаря Фасаду компилятора работа большинства программистов облегчается. При этом те, кому нужен доступ к средствам низкого уровня, не лишаются его.

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

Использование паттерна Фасад оправданно, когда:

  • • необходимо предоставить простой интерфейс к сложной подсистеме. Часто подсистемы усложняются по мере развития. Применение большинства паттернов приводит к появлению более простых классов, но в большем количестве. Такую подсистему проще повторно использовать и настраивать под конкретные нужды, но вместе с тем применять подсистему без настройки становится труднее. Фасад предлагает некоторый вид системы по умолчанию, устраивающий большинство клиентов. И лишь те объекты, которым нужны более широкие возможности настройки, могут обратиться напрямую к тому, что находится за Фасадом;
  • • между клиентами и классами реализации абстракции существует много зависимостей. Фасад позволяет отделить подсистему как от клиентов, так и от других подсистем, что, в свою очередь, способствует повышению степени независимости и переносимости;
  • • желательно разложить подсистему на отдельные слои. Фасад используется для определения точки входа на каждый уровень подсистемы. Если подсистемы зависят друг от друга, то зависимость можно упростить, разрешив подсистемам обмениваться информацией только через Фасады.

Структура

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

Схематичное объекта Фасад

Рис. 52. Схематичное объекта Фасад

Участники

Facade (Compiler) — Фасад:

  • • «знает», каким классам подсистемы адресовать запрос;
  • • делегирует запросы клиентов подходящим объектам внутри подсистемы.

Классы подсистемы (Scanner, Parser, ProgramNode и т. д.):

  • • реализуют функциональность подсистемы;
  • • выполняют работу, порученную объектом Facade;
  • • ничего не «знают» о существовании Фасада, т. е. не хранят ссылок на него.

Результаты

У паттерна Фасад есть следующие преимущества:

  • • изолирует клиентов от компонентов подсистемы, уменьшая тем самым число объектов, с которыми клиентам приходится иметь дело, и упрощая работу с подсистемой;
  • • позволяет ослабить связанность между подсистемой и ее клиентами. Зачастую компоненты подсистемы сильно связаны. Слабая связанность позволяет видоизменять компоненты, не затрагивая при этом клиентов. Фасад помогает разложить систему на слои и структурировать зависимости между объектами, а также избежать сложных и циклических зависимостей. Сокращение числа зависимостей за счет Фасадов может уменьшить количество нуждающихся в повторной компиляции файлов после небольшой модификации какой-нибудь важной подсистемы. Фасад может также упростить процесс переноса системы на другие платформы, поскольку уменьшается вероятность того, что в результате изменения одной подсистемы понадобится изменять и все остальные;
  • • Фасад не препятствует приложениям напрямую обращаться к классам подсистемы, если это необходимо. Таким образом, у вас есть выбор между простотой и общностью.

Реализация

При реализации Фасада следует обратить внимание на следующие вопросы:

  • уменьшение степени связанности клиента с подсистемой. Для этого надо сделать класс Facade абстрактным. Его конкретные подклассы будут соответствовать различным реализациям подсистемы. Это изолирует клиентов от информации о том, какая реализация подсистемы используется. Вместо порождения подклассов можно сконфигурировать объект Facade различными объектами подсистем. Для настройки Фасада достаточно заменить один или несколько таких объектов;
  • открытые и закрытые классы подсистем. Подсистема похожа на класс в том отношении, что у обоих есть интерфейсы и оба что-то инкапсулируют. Класс инкапсулирует состояние и операции, а подсистема — классы. И если полезно различать открытый и закрытый интерфейсы класса, то не менее разумно говорить об открытом и закрытом интерфейсах подсистемы. Открытый интерфейс подсистемы состоит из классов, к которым имеют доступ все клиенты; закрытый интерфейс доступен только для расширения подсистемы. Класс Facade является частью открытого интерфейса. Другие классы подсистемы также могут быть открытыми. Например, в системе компиляции классы Parser и Scanner — часть открытого интерфейса.

Отношения

Клиенты общаются с подсистемой, посылая запросы Фасаду. Он переадресует их подходящим объектам внутри подсистемы. Хотя основную работу выполняют именно объекты подсистемы, Фасаду, возможно, придется преобразовать свой интерфейс в интерфейсы подсистемы. Клиенты, пользующиеся Фасадом, не имеют прямого доступа к объектам подсистемы.

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

Пример паттерна Facade

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

Использование паттерна Facade

Определите для подсистемы простой, унифицированный интерфейс. Спроектируйте класс «обертку», инкапсулирующий подсистему.

Вся сложность подсистемы и взаимодействие ее компонентов скрыты от клиентов. «Фасад»/«обертка» переадресует пользовательские запросы подходящим методам подсистемы.

UML-диаграмма классов паттерна Decorator

Рис. 53. UML-диаграмма классов паттерна Decorator

Клиент использует только «Фасад».

Рассмотрите вопрос о целесообразности создания дополнительных «Фасадов».

Реализация паттерна 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, ElectricianlsNotDone, ElectricianDidltWrong, 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, TurnlnThePaperwork, Complete

};

int _state;

class FacilitiesDepartment

{

public:

void submitNetworkRequestO

{

_state = 0;

}

bool checkOnStatus()

{

_state++;

if (_state == Complete) return 1; return 0;

}

private: enum States {

Received, AssignToEngineer, EngineerResearches, RequestlsNotPossible, EngineerLeavesCompany, AssignToNewEngineer, NewEngineerResearches, Reassign Engineer, EngineerReturns, EngineerResearchesAgain, EngineerFillsOutPaperwork, Complete

};

int _state;

};

class FacilitiesFacade

{

public:

FacilitiesFacade()

{

_count = 0;

}

void submitNetworkRequestO

{

_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 -" << _c°unt << " 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

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

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

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

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

Abstract Factory может применяться как альтернатива Facade для сокрытия платформенно-зависимых классов.

Объекты «Фасадов» часто являются Singleton, потому что требуется только один объект Facade.

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

 
Если Вы заметили ошибку в тексте выделите слово и нажмите Shift + Enter
< Пред   СОДЕРЖАНИЕ   След >
 

Популярные страницы