Паттерн Facade (фасад)
Назначение паттерна Facade
- Паттерн Facade предоставляет унифицированный интерфейс вместо набора интерфейсов некоторой подсистемы. Facade определяет интерфейс более высокого уровня, упрощающий использование подсистемы.
- Паттерн Facade "обертывает" сложную подсистему более простым интерфейсом.
Решаемая проблема
Клиенты хотят получить упрощенный интерфейс к общей функциональности сложной подсистемы.
Обсуждение паттерна Facade
Паттерн Facade инкапсулирует сложную подсистему в единственный интерфейсный объект. Это позволяет сократить время изучения подсистемы, а также способствует уменьшению степени связанности между подсистемой и потенциально большим количеством клиентов. С другой стороны, если фасад является единственной точкой доступа к подсистеме, то он будет ограничивать возможности, которые могут понадобиться "продвинутым" пользователям.
Объект Facade, реализующий функции посредника, должен оставаться довольно простым и не быть всезнающим "оракулом".
Структура паттерна Facade
Клиенты общаются с подсистемой через Facade. При получении запроса от клиента объект Facade переадресует его нужному компоненту подсистемы. Для клиентов компоненты подсистемы остаются "тайной, покрытой мраком".
UML-диаграмма классов паттерна Facade
Подсистемы SubsystemOne и SubsystemThree не взаимодействуют напрямую с внутренними компонентами подсистемы SubsystemTwo. Они используют "фасад" SubsystemTwoWrapper (т.е. абстракцию более высокого уровня).
Пример паттерна Facade
Паттерн Facade определяет унифицированный высокоуровневый интерфейс к подсистеме, что упрощает ее использование. Покупатели сталкиваются с фасадом при заказе каталожных товаров по телефону. Покупатель звонит в службу поддержки клиентов и перечисляет товары, которые хочет приобрести. Представитель службы выступает в качестве "фасада", обеспечивая интерфейс к отделу исполнения заказов, отделу продаж и службе доставки.
Использование паттерна 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 <iostream.h>
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