Назначение шаблона Bridge - отделить абстракцию от ее реализации так, чтобы и то, и другое можно было изменять независимо.

Изображение с сайта http://ru.wikipedia.org
Проще всего понять, что такое шаблон Bridge, если вспомнить компоненту View в модели MVC.
Допустим, у нас есть текстовые объекты различных типов: Книги, статьи, динамически-формируемые страницы каталогов, справки и т.п. При этом, каждый документ может быть представлен в разных форматах: pdf, rtf, html и др.
Если мы начнем создавать иерархии классов вот так:
|
1 2 3 4 5 6 7 8 9 |
* Document * Document::Blank * Document::Blank::Html * Document::Blank::PDF * Document::Blank::XML * Document::Book * Document::Book::Html * Document::Book::Fb2 ... |
то модификация системы, добавление нового формата данных, или нового типа данных превращается в работу весьма сложную, чреватую дублированием кода, структура программы становится запутанной.
Именно поэтому, в данном случае будет полезно разделить саму структуру объектов и работу над их внешним видом.
У нас появляется новая структура:
|
1 2 3 4 5 6 7 8 |
* Document * Document::Blank * Document::Book * View * View::Html * View::PDF * View::XML * View::Fb2 |
В ней присутствует деление на абстракцию Abstraction (объекты документов) и реализацию Implementation (компоненты View).
Такая система легко расширяется.
Указанные принципы структурирования программы используются и в тех случаях, когда необходимо сделать переносимый графический интерфейс для какой-либо программы - создаются классы, которые будут отвечать за отрисовку интерфейса в разных средах.
Почему этот шаблон называют "Bridge" - мне не понятно. Этот шаблон так же имеет название "Handle/Body". Мне кажется, это чуть ближе по смыслу.
Пример реализации паттерна Bridge - 1
test.pl :
|
1 2 3 4 5 6 7 8 9 10 11 |
use strict; use Document::Book; use View::Html; use View::PDF; my $document = Document::Book->new(View::Html->new); $document->create; $document = Document::Book->new(View::PDF->new); $document->create; |
Document.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package Document; use strict; use warnings; sub new { my ($class, $imp) = @_; my $self = { imp => $imp, }; return bless $self, $class; } sub create { my $self = shift; $self->{imp}->operateImp; } 1; |
Document/Book.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package Document::Book; use parent Document; sub new { my ($class, $imp) = @_; my $self = $class->SUPER::new($imp); return $self; } sub operate { my $self = shift; $self->{imp}->operateImp; } 1; |
View.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package View; use strict; use warnings; sub new { my ($class, $args) = @_; my $self = {}; return bless $self, $class; } sub operateImp { die "ABSTRACT CLASS METHOD CANNOT BE CALLED DIRECTLY\n"; } 1; |
View/Html.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package View::Html; use parent View; use strict; use warnings; sub new { my ($class, $args) = @_; my $self = $class->SUPER::new($args); return $self; } sub operateImp { my $self = shift; print "Print document in html format\n"; } 1; |
View/PDF.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package View::PDF; use parent View; use strict; use warnings; sub new { my ($class, $args) = @_; my $self = $class->SUPER::new($args); return $self; } sub operateImp { my $self = shift; print "Print document in pdf format\n"; } 1; |
Запускаем скрипт на выполнение:
|
1 2 3 |
# test.pl Print document in html format Print document in pdf format |
Пример реализации паттерна Bridge - 2
Чистый пример, с соответствующей терминологией.
test.pl :
|
1 2 3 4 5 6 7 8 9 10 11 12 |
use strict; use Abstraction::RefinedAbstraction; use Implementator::ConcreteImplementatorA; use Implementator::ConcreteImplementatorB; my $abs = Abstraction::RefinedAbstraction->new( Implementator::ConcreteImplementatorA->new); $abs->operate; $abs = Abstraction::RefinedAbstraction->new( Implementator::ConcreteImplementatorB->new); $abs->operate; |
Abstraction.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 |
package Abstraction; use strict; use warnings; sub new { my ($class, $imp) = @_; my $self = { imp => $imp, }; return bless $self, $class; } sub implementator { my ($self, $imp) = @_; $self->{imp} = $imp if defined $imp; return $self->{imp}; } sub operate { my $self = shift; $self->{imp}->operateImp; } 1; |
Abstraction/RefinedAbstraction.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package Abstraction::RefinedAbstraction; use parent Abstraction; sub new { my ($class, $imp) = @_; my $self = $class->SUPER::new($imp); return $self; } sub operate { my $self = shift; print ref $self, " operate:\n"; $self->{imp}->operateImp; } 1; |
Implementator.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package Implementator; use strict; use warnings; sub new { my ($class, $args) = @_; my $self = {}; return bless $self, $class; } sub operateImp { die "ABSTRACT CLASS METHOD CANNOT BE CALLED DIRECTLY\n"; } 1; |
Implementator/ConcreteImplementatorA.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package Implementator::ConcreteImplementatorA; use parent Implementator; use strict; use warnings; sub new { my ($class, $args) = @_; my $self = $class->SUPER::new($args); return $self; } sub operateImp { my $self = shift; print ref $self, " operate Imp\n"; } 1; |
Implementator/ConcreteImplementatorB.pm :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package Implementator::ConcreteImplementatorB; use parent Implementator; use strict; use warnings; sub new { my ($class, $args) = @_; my $self = $class->SUPER::new($args); return $self; } sub operateImp { my $self = shift; print ref $self, " operate Imp\n"; } 1; |
Запускаем на выполнение:
|
1 2 3 4 5 |
# test.pl Abstraction::RefinedAbstraction operate: Implementator::ConcreteImplementatorA operate Imp Abstraction::RefinedAbstraction operate: Implementator::ConcreteImplementatorB operate Imp |