В чём суть паттерна проектирования «Мост» (Bridge)?

Ответ

Мост (Bridge) — это структурный паттерн проектирования, который разделяет один или несколько классов на две отдельные иерархии — абстракцию и реализацию, позволяя изменять их независимо друг от друга.

Проблема: Наследование жёстко связывает реализацию с абстракцией, что приводит к "разрастанию" классов (например, CircleBlue, CircleRed, SquareBlue, SquareRed).

Решение Bridge: Заменить наследование композицией, вынесли вариативную часть («реализацию») в отдельную иерархию.

Ключевые компоненты:

  1. Абстракция (Abstraction): Определяет интерфейс высокоуровневой логики и содержит ссылку на объект Реализации.
  2. Расширенная абстракция (Refined Abstraction): Вариации или расширения базовой абстракции.
  3. Реализация (Implementor): Интерфейс для всех конкретных реализаций.
  4. Конкретная реализация (Concrete Implementor): Конкретные классы, реализующие интерфейс Implementor.

Пример на Java (Фигуры и способы их рисования):

// 1. Implementor: Интерфейс реализации (инструмент рисования)
interface Renderer {
    String renderShape(String shapeName);
}

// 2. Concrete Implementor A
class VectorRenderer implements Renderer {
    public String renderShape(String shapeName) {
        return "Drawing " + shapeName + " as vector graphics.";
    }
}

// 3. Concrete Implementor B
class RasterRenderer implements Renderer {
    public String renderShape(String shapeName) {
        return "Rasterizing " + shapeName + " to pixels.";
    }
}

// 4. Abstraction: Абстракция фигуры
abstract class Shape {
    protected Renderer renderer; // Мост!

    protected Shape(Renderer renderer) {
        this.renderer = renderer;
    }

    public abstract String draw();
}

// 5. Refined Abstraction
class Circle extends Shape {
    private String name;

    public Circle(Renderer renderer, String name) {
        super(renderer);
        this.name = name;
    }

    @Override
    public String draw() {
        return renderer.renderShape("Circle: " + name); // Делегирование реализации
    }
}

// Клиентский код
public class BridgeDemo {
    public static void main(String[] args) {
        Shape vectorCircle = new Circle(new VectorRenderer(), "Small Circle");
        Shape rasterCircle = new Circle(new RasterRenderer(), "Large Circle");

        System.out.println(vectorCircle.draw()); // Drawing Circle: Small Circle as vector graphics.
        System.out.println(rasterCircle.draw());  // Rasterizing Circle: Large Circle to pixels.
    }
}

Преимущества:

  • Разделение ответственности: Абстракция и реализация развиваются независимо.
  • Принцип открытости/закрытости: Можно вводить новые абстракции и реализации, не изменяя существующий код.
  • Принцип единственной ответственности: Абстракция занимается высокоуровневой логикой, реализация — деталями платформы.

Применение: Часто используется в драйверах, графических библиотеках, когда нужно поддерживать несколько типов платформ или API.

Ответ 18+ 🔞

Так, слушай, смотри, есть у нас паттерн «Мост». А звучит-то как, блядь, — «мост»! Прям как будто мы через речку к бабушке идём, а не код пишем. Но нет, это про другое, ёпта.

Представь себе, что ты делаешь графический редактор. У тебя есть фигуры — круг, квадрат, треугольник. И есть способы их рисовать — векторный, растровый, может, ещё какая-нибудь нейросеть-хуесеть.

И вот сидит такой разработчик и думает: «А сделаю-ка я класс CircleVector, CircleRaster, SquareVector, SquareRaster...». И понеслась, блядь! Классов становится овердохуища, а если добавить ещё один способ рисования — так это пиздец, в рот меня чих-пых! Половина из них будет отличаться одной строчкой, а копипаста — мать родная. Это и есть та самая проблема.

А решение-то где? А решение, блядь, гениальное в своей простоте. Давай разведём эти две хуйни по разным углам! Одна иерархия будет отвечать за что рисовать (абстракция — фигуры), а вторая — за как рисовать (реализация — рендереры). А свяжем мы их не наследованием, а композицией — то есть просто вставим одну штуку внутрь другой. Это и есть тот самый мост, ёбана!

Смотри на примере, тут всё понятно становится.

// 1. Это наша "реализация". То, КАК будем рисовать. Интерфейс для всех рисовалок.
interface Renderer {
    String renderShape(String shapeName);
}

// 2. Конкретная реализация А: Вектор, чтоб его.
class VectorRenderer implements Renderer {
    public String renderShape(String shapeName) {
        return "Рисую " + shapeName + " как вектор, красиво, чётко.";
    }
}

// 3. Конкретная реализация Б: Растр, для пиксельных дегенератов.
class RasterRenderer implements Renderer {
    public String renderShape(String shapeName) {
        return "Пикселюю " + shapeName + ", вот тебе квадратики, доволен?";
    }
}

// 4. А это наша "абстракция". То, ЧТО будем рисовать. Фигура. У неё внутри ЗАСЫЛАН рендерер!
abstract class Shape {
    protected Renderer renderer; // ВОТ ОН, МОСТ, СУКА! Ссылка на реализацию.

    protected Shape(Renderer renderer) {
        this.renderer = renderer; // Подсовываем рендерер извне. Гибко, блядь!
    }

    public abstract String draw(); // А это уже высокоуровневая команда "нарисовать".
}

// 5. Уточнённая абстракция. Конкретная фигура — круг.
class Circle extends Shape {
    private String name;

    public Circle(Renderer renderer, String name) {
        super(renderer); // Передаём рендерер в базовый класс.
        this.name = name;
    }

    @Override
    public String draw() {
        // И тут вся магия: фигура НЕ РИСУЕТ САМА. Она говорит рендереру: "Слушай, дружок, нарисуй-ка мне кружочек".
        return renderer.renderShape("Круг '" + name + "'");
    }
}

// Ну и смотрим, как это пашет.
public class BridgeDemo {
    public static void main(String[] args) {
        // Круг с векторным рендерером
        Shape vectorCircle = new Circle(new VectorRenderer(), "Маленький кружок");
        // Круг с растровым рендерером
        Shape rasterCircle = new Circle(new RasterRenderer(), "Большой кружок");

        System.out.println(vectorCircle.draw()); // Рисую Круг 'Маленький кружок' как вектор...
        System.out.println(rasterCircle.draw());  // Пикселюю Круг 'Большой кружок'...
    }
}

Видишь, в чём прикол? Мы теперь можем лепить фигуры и рендереры как конструктор «Лего». Хочешь треугольник в векторе? new Triangle(new VectorRenderer()). Хочешь квадрат в растре? new Square(new RasterRenderer()). Добавил новый рендерер, например, 3DRenderer? И все существующие фигуры сразу научились им рисоваться! Это же ёперный театр, а не жизнь!

Чем это, блядь, хорошо?

  • Развёл хуйню по полочкам. Фигуры живут своей жизнью, рендереры — своей. Не мешают друг другу.
  • Расширяемо, сука. Добавил новый рендерер — ни одну старую фигуру не пришлось трогать. Принцип открытости/закрытости в действии, ебать его в сраку.
  • Чистота. Каждый класс делает своё дело. Фигура — управляет собой, рендерер — рисует. Никакой спагетти-код.

Где это применяется? Да везде, где есть какая-то платформенно-зависимая хуйня! Драйвера баз данных, разные GUI-фреймворки, поддержка API. В общем, когда нужно отвязать высокоуровневую логику от низкоуровневой реализации, чтобы они не ебали друг другу мозг.