Что такое паттерн "Мост" (Bridge)

Мост — структурный паттерн проектирования, который разделяет абстракцию и её реализацию так, чтобы их можно было изменять независимо друг от друга. Это помогает избежать жесткой связи между ними и делает код более гибким и расширяемым.

Представьте, что у вас есть разные варианты абстракций и реализаций, и они могут комбинироваться произвольно. Вместо создания множества классов для каждой комбинации используется паттерн мост — абстракция содержит ссылку на реализацию, и они развиваются отдельно.

Преимущества паттерна «Мост»

  • Разделение абстракции и реализации облегчает поддержку и расширение.
  • Позволяет изменять обе части независимо.
  • Уменьшает количество классов при множественных вариациях абстракций и реализаций.

Пример:

  • Абстракция: интерфейс рисования фигуры.
  • Реализации: разные способы отрисовки (например, на экране, в файле).
  • Мост связывает фигуру и конкретный способ отрисовки.

Где применяется паттерн «Мост»?

  • Графические библиотеки и UI-фреймворки.
  • Системы с разными способами реализации функционала.
  • Кросс-платформенные приложения.

Пример реализации на JavaScript

// Реализация интерфейса рисования class DrawingAPI { drawCircle(x, y, radius) { throw new Error("Метод drawCircle() должен быть реализован"); } } // Конкретная реализация 1 class DrawingAPI1 extends DrawingAPI { drawCircle(x, y, radius) { console.log( `Рисуем круг с помощью DrawingAPI1 в (${x}, ${y}) с радиусом ${radius}`, ); } } // Конкретная реализация 2 class DrawingAPI2 extends DrawingAPI { drawCircle(x, y, radius) { console.log( `Рисуем круг с помощью DrawingAPI2 в (${x}, ${y}) с радиусом ${radius}`, ); } } // Абстракция — фигура class Shape { constructor(drawingAPI) { this.drawingAPI = drawingAPI; } draw() { throw new Error("Метод draw() должен быть реализован"); } resizeByPercentage(pct) { throw new Error("Метод resizeByPercentage() должен быть реализован"); } } // Конкретная фигура — круг class CircleShape extends Shape { constructor(x, y, radius, drawingAPI) { super(drawingAPI); this.x = x; this.y = y; this.radius = radius; } draw() { this.drawingAPI.drawCircle(this.x, this.y, this.radius); } resizeByPercentage(pct) { this.radius *= pct; } } // Использование const api1 = new DrawingAPI1(); const api2 = new DrawingAPI2(); const circle1 = new CircleShape(5, 10, 10, api1); circle1.draw(); circle1.resizeByPercentage(2); circle1.draw(); const circle2 = new CircleShape(20, 30, 15, api2); circle2.draw();

Когда стоит использовать паттерн «Мост»?

  • Когда нужно разделить абстракцию и реализацию, чтобы развивать их независимо.
  • Когда требуется поддержка множества вариантов абстракций и реализаций.
  • Чтобы избежать комбинаторного взрыва классов при множественных вариациях.

Отличие от других паттернов

  • В отличие от адаптера, который преобразует интерфейс, мост связывает абстракцию с реализацией. Мост обеспечивает более гибкую структуру, позволяя менять обе части отдельно.

Плюсы паттерна

  • Улучшает расширяемость и поддержку кода.
  • Снижает зависимость между абстракцией и реализацией.
  • Уменьшает количество классов по сравнению с прямым наследованием.

Минусы паттерна

  • Усложняет структуру за счёт дополнительного уровня абстракции.
  • Может быть избыточен для простых систем с небольшим количеством вариантов.