Что такое паттерн проектирования Bridge (Мост)?

Ответ

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

Проблема: Наследование жёстко привязывает реализацию к абстракции. Добавление новой реализации или абстракции требует создания множества классов.

Решение: Bridge заменяет наследование композицией. Абстракция содержит ссылку на объект реализации.

Пример на Swift:

// 1. Иерархия Реализации (Implementor)
protocol Renderer {
    func renderCircle(radius: Double)
}

class VectorRenderer: Renderer {
    func renderCircle(radius: Double) {
        print("Рисую круг радиусом (radius) как векторную графику.")
    }
}

class RasterRenderer: Renderer {
    func renderCircle(radius: Double) {
        print("Рисую круг радиусом (radius) как растровое изображение (пиксели).")
    }
}

// 2. Иерархия Абстракции (Abstraction)
class Shape {
    let renderer: Renderer
    init(renderer: Renderer) {
        self.renderer = renderer
    }
    func draw() {}
}

class Circle: Shape {
    var x, y, radius: Double

    init(x: Double, y: Double, radius: Double, renderer: Renderer) {
        self.x = x
        self.y = y
        self.radius = radius
        super.init(renderer: renderer)
    }

    override func draw() {
        renderer.renderCircle(radius: radius)
    }
}

// Использование
let vector = VectorRenderer()
let raster = RasterRenderer()

let circle1 = Circle(x: 0, y: 0, radius: 5, renderer: vector)
circle1.draw() // Рисую круг радиусом 5.0 как векторную графику.

let circle2 = Circle(x: 10, y: 10, radius: 10, renderer: raster)
circle2.draw() // Рисую круг радиусом 10.0 как растровое изображение (пиксели).

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

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

Ответ 18+ 🔞

А, паттерн Bridge, говоришь? Ну это ж классика, блядь! Ситуация, когда твоя абстракция и реализация срослись как сиамские близнецы, и ты уже не можешь ни туды, ни сюды, блядь.

Представь, пишешь ты код для рисования фигур. Сначала всё просто: class Circle, class Square. Потом надо добавить, что они могут рисоваться векторно или растрово. И ты такой: "Да не вопрос, ёпта!" — и лепишь class VectorCircle, class RasterCircle, class VectorSquare, class RasterSquare... А потом ещё добавить фигуру Triangle, и тебе надо VectorTriangle и RasterTriangle. Короче, хуйня полная, блядь! Классов как говна за баней, а добавить что-то новое — это пиздец, нахуй, переписывать пол-проекта.

И тут выходит на сцену наш спаситель — Bridge, ёбана! Суть в чём, блядь? Отделить абстракцию (что мы делаем) от реализации (как мы это делаем). Чтобы они жили своей жизнью, как соседи по коммуналке, которые друг другу не мешают, но хуй знает как.

Смотри, как это выглядит в коде, блядь. Сначала делаем иерархию для реализации — то есть как рисовать.

protocol Renderer {
    func renderCircle(radius: Double)
}

class VectorRenderer: Renderer {
    func renderCircle(radius: Double) {
        print("Рисую круг радиусом (radius) как векторную графику.")
    }
}

class RasterRenderer: Renderer {
    func renderCircle(radius: Double) {
        print("Рисую круг радиусом (radius) как растровое изображение (пиксели).")
    }
}

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

Теперь вторая часть — абстракция. То есть что мы рисуем.

class Shape {
    let renderer: Renderer // Вот он, мост, блядь! Ссылка на реализацию.
    init(renderer: Renderer) {
        self.renderer = renderer
    }
    func draw() {}
}

class Circle: Shape {
    var x, y, radius: Double

    init(x: Double, y: Double, radius: Double, renderer: Renderer) {
        self.x = x
        self.y = y
        self.radius = radius
        super.init(renderer: renderer) // Передаём того, кто будет рисовать
    }

    override func draw() {
        renderer.renderCircle(radius: radius) // А вот и делегируем, блядь!
    }
}

Видишь магию, ёпта? Класс Circle не заботится, как его нарисовать. Он просто говорит своему rendererу: "Э, сабака сука, нарисуй-ка мне круг вот такого радиуса!". А тот уж сам решает, векторами ему это делать или пикселями. Полная независимость, блядь!

Используется это вот так, просто пиздец как элегантно:

let vector = VectorRenderer()
let raster = RasterRenderer()

let circle1 = Circle(x: 0, y: 0, radius: 5, renderer: vector)
circle1.draw() // Рисую круг радиусом 5.0 как векторную графику.

let circle2 = Circle(x: 10, y: 10, radius: 10, renderer: raster)
circle2.draw() // Рисую круг радиусом 10.0 как растровое изображение (пиксели).

Хочешь добавить новый способ рисования? Пожалуйста, блядь! Создай класс HolographicRenderer и реализуй тот же протокол Renderer. Все существующие фигуры сразу смогут им пользоваться, нихуя не меняясь!

Хочешь добавить новую фигуру Square? Да хуй с ним, создавай! Она будет принимать в конструктор любого Rendererа и делегировать ему рисование. Никакого взрыва мозга, никакого комбинаторного роста классов.

В чём профит, блядь?

  • Принцип открытости/закрытости в деле: Можно расширять систему в двух направлениях (и фигуры, и способы рисования), не ломая то, что уже работает. Во, блядь!
  • Спрятано под капотом: Клиенту, который создаёт круг, похуй, как он рисуется. Он просто говорит "нарисуй", а детали — это проблемы моста.
  • Связанность на нуле: Абстракция и реализация живут отдельно, как кот и собака. Одна может меняться, не трогая другую. Идеал, ёпта!

Вот и весь мост, блядь. Не путай его с адаптером или декоратором, а то опять в рот меня чих-пых! Это именно разделение, чтобы не плодить сущностей, как говна после борща.