Во время исследования Моста, я обнаружил, что он появляется для большинства неудачно описанных шаблонов в GoF. Я пришел к этому выводу после прочтения главы Алана Шеловэя (Alan Shalloway) о Мостах в его книге "Design Patterns Explained" - он указывает на то, что описание осталось им не освещенным в GoF.
На конференции я поговорил с двумя людьми, которые уже писали и говорили о шаблонах проектирования, включая Мост. В двух независимых дискуссиях я получил полностью различную перспективу структуры Моста.
Вооружившись отсутствием информации, я снова погрузился в GoF и обнаружил, что ни одна из этих перспектив не согласуется с этой книгой. Также я нашел, что книга делает неполную попытку описания Моста, исключая одно место - не в описании основной структуры диаграммы шаблона, которая не очень-то полезна, а в диаграмме структуры, описывающей их специфический пример. Только если вы присмотритесь к тому, что называют Мостом, то начнете понимать смысл.
Важнейшей особенностью в понимании, когда вы рассматриваете Мост (Bridge), является то, что часто эта конструкция используется для помощи в написании кода. Вы можете выбрать объекты, которые вы используете для определенной ситуации во время компиляции или во время выполнения, но цель Моста состоит в предоставлении вам такой структуры кода, которая поможет легко добавлять новые виды объектов переднего плана, которые реализуются с функциональностью новых объектов заднего плана. Таким образом, и объекты переднего плана, и объекты заднего плана могут изменяться независимо друг от друга.
Классы объектов переднего плана могут иметь полностью отличающийся интерфейс от других классов, и обычно отличаются. То, что они делают, в общем - они могут реализовать свою функциональность, используя возможности любого числа различных объектов заднего плана. Объекты заднего плата также не имеют общего интерфейса. Единственной вещью, которой должны обладать объекты заднего плана является то, что они реализуют функциональность одного вида - например, группа различных способов реализации графической библиотеки или набор различных решений по хранению данных.
Мост (Bridge) на самом деле является инструментом организации кода, который позволяет вам добавлять любое число новых сервисов переднего плана, которые реализуют свои операции, делегируя их произвольному количеству опций заднего плана. Используя Мост, вы можете достигнуть совершенства без обычного взрывного увеличения комбинаций возможностей, которое может произойти в противном случае. Но держите в уме, что вектор изменений при использовании Моста обычно происходит в момент кодирования: он сохраняет ваш код организованным, когда вы решаете увеличить число опций для реализации функциональность.
Ниже приведен пример, единственным назначением которого является демонстрация структуры Моста (он реализует приведенную выше диаграмму):
//: bridge:BridgeStructure.java
// Демонстрация структуры и операций
// Шаблона Моста.
package bridge;
import junit.framework.*;
class Abstraction {
private Implementation implementation;
public Abstraction(Implementation imp) {
implementation = imp;
}
// Абстракция используется различными объектами
// переднего плана для своей реализации
// различных интерфейсов.
public void service1() {
// Реализация этой возможности использует некую
// комбинацию реализаций заднего плана:
implementation.facility1();
implementation.facility2();
}
public void service2() {
// Реализация этой возможности использует некую
// другую комбинацию реализаций заднего плана:
implementation.facility2();
implementation.facility3();
}
public void service3() {
// Реализация этой возможности использует некую
// другую комбинацию реализаций заднего плана:
implementation.facility1();
implementation.facility2();
implementation.facility4();
}
// Для использования подклассов:
protected Implementation getImplementation() {
return implementation;
}
}
class ClientService1 extends Abstraction {
public ClientService1(Implementation imp) {
super(imp);
}
public void serviceA() {
service1();
service2();
}
public void serviceB() {
service3();
}
}
class ClientService2 extends Abstraction {
public ClientService2(Implementation imp) {
super(imp);
}
public void serviceC() {
service2();
service3();
}
public void serviceD() {
service1();
service3();
}
public void serviceE() {
getImplementation().facility3();
}
}
interface Implementation {
// Эта общая реализация обеспечивается
// объектами заднего плана, каждый - своим собственным способом.
void facility1();
void facility2();
void facility3();
void facility4();
}
class Library1 {
public void method1() {
System.out.println("Library1.method1()");
}
public void method2() {
System.out.println("Library1.method2()");
}
}
class Library2 {
public void operation1() {
System.out.println("Library2.operation1()");
}
public void operation2() {
System.out.println("Library2.operation2()");
}
public void operation3() {
System.out.println("Library2.operation3()");
}
}
class Implementation1 implements Implementation {
// Каждая способность делегируется разным библиотекам
// для того, чтобы получить дивиденды.
private Library1 delegate = new Library1();
public void facility1() {
System.out.println("Implementation1.facility1");
delegate.method1();
}
public void facility2() {
System.out.println("Implementation1.facility2");
delegate.method2();
}
public void facility3() {
System.out.println("Implementation1.facility3");
delegate.method2();
delegate.method1();
}
public void facility4() {
System.out.println("Implementation1.facility4");
delegate.method1();
}
}
class Implementation2 implements Implementation {
private Library2 delegate = new Library2();
public void facility1() {
System.out.println("Implementation2.facility1");
delegate.operation1();
}
public void facility2() {
System.out.println("Implementation2.facility2");
delegate.operation2();
}
public void facility3() {
System.out.println("Implementation2.facility3");
delegate.operation3();
}
public void facility4() {
System.out.println("Implementation2.facility4");
delegate.operation1();
}
}
public class BridgeStructure extends TestCase {
public void test1() {
// Здесь реализация определяется
// клиентом во время создания:
ClientService1 cs1 = new ClientService1(new Implementation1());
cs1.serviceA();
cs1.serviceB();
}
public void test2() {
ClientService1 cs1 = new ClientService1(new Implementation2());
cs1.serviceA();
cs1.serviceB();
}
public void test3() {
ClientService2 cs2 = new ClientService2(new Implementation1());
cs2.serviceC();
cs2.serviceD();
cs2.serviceE();
}
public void test4() {
ClientService2 cs2 = new ClientService2(new Implementation2());
cs2.serviceC();
cs2.serviceD();
cs2.serviceE();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(BridgeStructure.class);
}
} // /:~
Базовый класс переднего плана обеспечивает операции, используемые для выполнения производными классами переднего плана в терминах методов базового классов заднего плана. Таким образом, любой производный класс заднего плана может быть использован для выполнения операции, необходимой классу переднего плана. Заметьте, что мост возникает в последовательности шагов, каждый из которых обеспечивает уровень абстракции. В данном примере Implementation определяется как интерфейс для того, чтобы подчеркнуть, что вся функциональность реализуется производными классами заднего плана, а не базовым классом заднего плана.
Производные классы заднего плана выполняют операции, определенные в базовом классе, делегируя их объектам (классов Library1 и Library2, в данном случае), которые обычно имеют радикально различающийся интерфейс, но чаще всего предоставляют одинаковую функциональность (это одно из важных требования Моста). В реальности, каждая реализация заднего плана является адаптером к различным библиотекам или инструментам, используемым для реализации определенной функциональности различными способами.
← | Адаптер (Adapter) | Упражнения | → |