Шаблон проектирования Facade используется для предоставления простого в использовании интерфейса к сложному набору интерфейсов и подсистем. Шаблон позволяет скрыть детали реализации систем, упрощая работу с конечным продуктом.
Краткое описание
Допустим, у нас есть некоторое программное обеспечение, которое предоставляет доступ к системе компиляции программного кода. Каждый компилятор имеет множество подклассов: лексический анализатор, синтаксический анализатор, и т.п. Возможно, некоторым специализированным программам может понадобиться прямой доступ к этим подклассам, но в большинстве случаев, клиентам компилятора знание таких подробностей не требуется — необходимо просто скомпилировать заданный программный код.
Поэтому, имеет смысл предоставить клиентам удобный интерфейс, который будет изолировать классы компилятора от клиента. В систему просто включается дополнительный класс ProgramRunner. Он определяет унифицированный интерфейс ко всем возможностям компилятора. В этом случае, класс ProgramRunner будет выступать в роли фасада — предлагать более простой интерфейс к достаточно сложной подсистеме.
Клиенты общаются с подсистемой, посылая запросы фасаду. Он переадресует их подходящим объектам внутри подсистемы.
До применения паттерна:
1 2 3 4 5 |
my $compiler = Compiler->new(); my $bytecode = $compiler->compile('file'); my $interpeter = Interpreter->new(); $interpreter->execute($bytecode) |
После применения паттерна:
1 2 |
my $runner = ProgramRunner->new(); $runner->run('file') |
Пример «из жизни»
Паттерн Facade используется в таких популярных системах, как LWP и DBI.
LWP
Для того, чтобы получить простой файл с сайта, надо установить соединение с этим ресурсом, создать корректный http-запрос, получить http-ответ, разобрать ответ и извлечь из него нужные данные. Если требуется обработать cookies, отправить данные из html-формы, количество работы становится еще больше. Для выполнения всей этой работы требуется подключить множество классов и оперировать множеством объектов. Если в рамках программы требуется реализовать всего лишь разовое подключение к сайту, это как минимум не удобно и не эффективно.
Модуль LWP представляет собой фасад ко множеству объектов, которые реализуют всю работу по подключению к нужному ресурсу, отправке запросов и получению ответов.
Можно упростить работу еще больше — LWP::Simple представляет собой фасад для LWP::UserAgent и др.
Пример:
1 2 3 4 |
use LWP::Simple qw(get); my $url = "http://dev-lab.info"; my $data = get($url); |
Кроме того, LWP является фасадом для использования различных протоколов HTTP, HTTPS, FTP, NNTP, и разработчику не приходится тратить время на подключение различных библиотек и изучение особенностей работы с ними.
DBI
Архитектура DBI:
1 2 3 4 5 6 7 8 9 10 11 |
|<- Scope of DBI ->| .-. .--------------. .-------------. .-------. | |---| XYZ Driver |---| XYZ Engine | | Perl | | | `--------------' `-------------' | script| |A| |D| .--------------. .-------------. | using |--|P|--|B|---|Oracle Driver |---|Oracle Engine| | DBI | |I| |I| `--------------' `-------------' | API | | |... |methods| | |... Other drivers `-------' | |... `-' |
DBI обеспечивает простой интерфейс доступа к различным серверам баз данных. При этом не нужно беспокоиться о реализации подключения к базе данных, реализации протоколов взаимодействия и формате данных.
Можно даже изменить тип базы данных и ограничиться внесением минимальных изменений в свой код, или даже вообще обойтись без них.
Class::Facade
Этот модуль реализует простой класс — Facade, который позволяет создавать объекты, делегирующие выполнение работы другим классам и объектам.
Синтаксис Class::Facade:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
use Class::Facade; my $facade = Class::Facade->new({ method1 => sub { ... }, method2 => [ $class, $method, $arg1, $arg2, ... ], method3 => [ $object, $method, $arg1, $arg2, ... ], method4 => { class => 'My::Delegate::Class', method => 'method_name', args => [ $arg1, $arg2, ... ], }, method5 => { object => $object, method => 'method_name', args => [ $arg1, $arg2, ... ], }, }); $facade->method1($more_args1, ...); $facade->method2($more_args2, ...); |
Использование Class::Facade :
1 2 3 4 5 6 7 8 9 |
package My::Facade::One; use base qw( Class::Facade ); package My::Facade::Two; use base qw( Class::Facade ); package main; my $one = My::Facade::One->new({ ... }); my $two = My::Facade::Two->new({ ... }); |
Очень простой пример реализации паттерна Facade
Frequest.pm :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package Frequest; use strict; sub new { my $type=shift; my $self; $self = bless( {}, $type); return $self; } sub send_request { my $self = shift; print "Log: send request\n"; return 1; } 1; |
Fresponse.pm :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package Fresponse; use strict; sub new { my $type=shift; my $self; $self = bless( {}, $type); return $self; } sub get_response { my $self = shift; print "Log: get response\n"; return 1; } 1; |
Фасад — Facade.pm :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package Facade; use strict; use Frequest; use Fresponse; sub new { my $type=shift; my $self; $self = bless( {}, $type); return $self; } sub process { my $self = shift; Frequest->new->send_request; Fresponse->new->get_response; print "Process... done\n"; return 1; } 1; |
Вместо вызова нескольких модулей, вызываем только модуль Facade.pm.
facade.pl :
1 2 3 4 5 6 7 8 9 |
#!/usr/bin/perl use strict; use Facade; my $obj = Facade->new; $obj->process(); exit; |
Полезные ссылки по теме «шаблон проектирования Facade»
theperlreview.com: The Facade Design Pattern
theperlreview.com: The Facade Design Pattern (PDF)
houseabsolute.com: Facade example