JavaRush /Java блог /Random UA /Паттерн проектування Міст (Bridge Pattern)

Паттерн проектування Міст (Bridge Pattern)

Стаття з групи Random UA
Вітання! Продовжуємо розумітися на великій і дуже корисній темі — патерни проектування. Сьогодні поговоримо про Bridge. Як і інші патерни, Bridge слугує для вирішення типових проблем, з якими стикається розробник під час проектування архітектури програмного забезпечення. Давай сьогодні вивчимо його особливості та дізнаємося, як його варто використати.

Що являє собою патерн Bridge?

Паттерн Bridge (Міст) – структурний шаблон проектування. Тобто його основне завдання — створення повноцінної структури із класів та об'єктів. Bridge вирішує це завдання шляхом поділу одного або декількох класів на окремі ієрархії - абстракцію та реалізацію . Зміна функціоналу в одній ієрархії не тягне за собою зміни до іншої. Начебто все зрозуміло, але за фактом це визначення звучить дуже широко і не дає відповіді на головне питання: "Що являє собою патерн Bridge?" Думаю, з цим тобі простіше буде розібратися на практиці. Давай відразу змоделюємо класичний приклад для патерну Bridge. У нас є абстрактний клас Shape, який узагальнено описує геометричну фігуру:
  • Shape.java

    public abstract class Shape {
       public abstract void draw();
    }

    Коли ми вирішимо додати фігури трикутника та прямокутника, ми успадкуємося від класу Shape:

  • Rectangle.java:

    public class Rectangle extends Shape {
       @Override
       public void draw() {
           System.out.println("Drawing rectangle");
       }
    }
  • Triangle.java:

    public class Triangle extends Shape {
       @Override
       public void draw() {
           System.out.println("Drawing triangle");
       }
    }
Виглядає все просто доти, поки ми не вводимо поняття “кольору”. Тобто, кожна фігура матиме свій колір, від якого залежатиме функціонал методу draw(). Щоб мати різні реалізації методу draw(), нам необхідно створити клас для кожної фігури, що відповідає кольору. Якщо три кольори, то шість класів: TriangleBlack, TriangleGreen, TriangleRed, RectangleBlack, RectangleGreenі RectangleRed. Шість класів — не така вже й велика проблема. Але! Якщо нам потрібно буде додати нову фігуру чи колір, кількість класів зростатиме у геометричній прогресії. Як вийти із ситуації? Зберігання кольору в полі та перебір варіантів через умовні конструкції – не найкращий вихід. Хороше рішення - вивести колір в окремий інтерфейс . Сказано - зроблено: давай створимо інтерфейсColorі три його імплементації - BlackColor, GreenColorі RedColor:
  • Color.java:

    public interface Color {
       void fillColor();
    }
  • BlackColor.java:

    public class BlackColor implements Color {
       @Override
       public void fillColor() {
           System.out.println("Filling in black color");
       }
    }
  • GreenColor.java

    public class GreenColor implements Color {
       @Override
       public void fillColor() {
           System.out.println("Filling in green color");
       }
    }
  • RedColor.java

    public class RedColor implements Color {
       @Override
       public void fillColor() {
           System.out.println("Filling in red color");
       }
    }

    Тепер додамо поле типу Colorдо класу Shape— його значення отримуватимемо в конструкторі.

  • Shape.java:

    public abstract class Shape {
       protected Color color;
    
       public Shape(Color color) {
           this.color = color;
       }
    
       public abstract void draw();
    }

    Змінну colorми будемо використовувати у реалізаціях Shape. А це означає, що фігури тепер можуть використовувати функціонал інтерфейсу Color.

  • Rectangle.java

    public class Rectangle extends Shape {
    
       public Rectangle(Color color) {
           super(color);
       }
    
       @Override
       public void draw() {
           System.out.println("Drawing rectangle");
           color.fillColor();
       }
    }
Ну ось! Тепер ми можемо плодити різні кольори та геометричні фігури хоч до нескінченності, збільшуючи кількість класів в арифметичній прогресії. Поле Color colorє мостом (bridge), який взаємозв'язує дві окремі ієрархії класів.

Пристрій Bridge: що таке абстракція та реалізація

Давайте розглянемо з тобою діаграму класів, яка описує патерн Bridge: Знайомство з патерном проектування Bridge - 2Тут можна побачити дві незалежні структури, які можуть модифікуватися, не торкаючись функціонал один одного. У нашому випадку це:
  • Abstraction - клас Shape;
  • RefinedAbstraction - класи Triangle,;Rectangle
  • Implementor - інтерфейс Color;
  • ConcreteImplementor - класи BlackColor, GreenColorі RedColor.
Клас Shapeявляє собою абстракцію - механізм управління розфарбуванням фігур у різні кольори, який делегує реалізацію інтерфейсу Color. Класи є реальними об'єктами Triangle, Rectangleякі використовують механізм, запропонований класом Shape. BlackColor, GreenColorІ RedColor- конкретні імплементації у гілці Реалізація. Їх часто називають платформою.

Де використовують патерн Bridge

Величезний плюс використання цього патерну полягає в тому, що можна вносити зміни до функціоналу класів однієї гілки, не ламаючи при цьому логіку іншої. Також такий підхід допомагає зменшити пов'язаність класів програми. Головна умова застосування патернів - "слідувати інструкції": не пхати їх абияк! Власне, давай розберемося, у яких випадках точно потрібно використовувати Bridge:
  1. Якщо необхідно розширити кількість сутностей на дві сторони (геометричні фігури, кольори).

  2. Якщо є бажання розділити великий клас, який не відповідає принципу Single responsibility, на більш маленькі класи з вузькопрофільним функціоналом.

  3. При можливій необхідності вносити зміни до логіки роботи деяких сутностей під час роботи програми.

  4. За потреби сховати реалізацію від клієнтів класу (бібліотеки).

При використанні патерну щоразу потрібно пам'ятати, що він додає додаткові сутності в код — не зовсім логічно застосовувати його в проекті, де лише одна геометрична фігура та один-два можливі її кольори.

Плюси та мінуси патерну

Як і інші патерни, Мост має і переваги, і недоліки. Переваги Bridge:
  1. Покращує масштабованість коду – можна додавати функціонал, не боячись зламати щось в іншій частині програми.
  2. Зменшує кількість підкласів – працює при необхідності розширення кількості сутностей у дві сторони (наприклад, кількість фігур та кількість кольорів).
  3. Дає можливість окремо працювати над двома самостійними гілками Абстракції та Реалізації – це можуть робити два різні розробники, не вникаючи в деталі коду один одного.
  4. Зменшення зв'язаності класів - єдине місце зв'язки двох класів - це міст (поле Color color).
Недоліки Bridge:
  1. Залежно від конкретної ситуації та структури проекту в цілому, можливий негативний вплив на продуктивність програми (наприклад, якщо потрібно ініціалізувати більшу кількість об'єктів).
  2. Ускладнює читання коду через необхідність навігації між класами.

На відміну від патерну Strategy

Паттерн Bridge часто плутають з іншим шаблоном проектування Strategy. Вони обидва використовують композицію (у прикладі з фігурами та кольорами ми використовували агрегацію, але Bridge може використовувати і композицію), делегуючи роботу іншим об'єктам. Але різниця між ними є, і вона величезна. Паттерн Strategy є поведінковим патерном: він вирішує зовсім інші завдання. Strategy забезпечує взаємозамінність алгоритмів, тоді як Bridge відокремлює абстракцію від реалізації, щоб забезпечити можливість вибору між різними імплементаціями. Тобто, Bridge, на відміну від Strategy, застосовується до цілих конструкцій чи ієрархічних структур. Паттерн Bridge може стати гарною зброєю в арсеналі розробника, головне намацати ті ситуації, де варто застосувати його, або скористатися якимось іншим шаблоном.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ