Що являє собою патерн 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: Тут можна побачити дві незалежні структури, які можуть модифікуватися, не торкаючись функціонал один одного. У нашому випадку це:- Abstraction - клас
Shape
; - RefinedAbstraction - класи
Triangle
,;Rectangle
- Implementor - інтерфейс
Color
; - ConcreteImplementor - класи
BlackColor
,GreenColor
іRedColor
.
Shape
являє собою абстракцію - механізм управління розфарбуванням фігур у різні кольори, який делегує реалізацію інтерфейсу Color
. Класи є реальними об'єктами Triangle
, Rectangle
які використовують механізм, запропонований класом Shape
. BlackColor
, GreenColor
І RedColor
- конкретні імплементації у гілці Реалізація. Їх часто називають платформою.
Де використовують патерн Bridge
Величезний плюс використання цього патерну полягає в тому, що можна вносити зміни до функціоналу класів однієї гілки, не ламаючи при цьому логіку іншої. Також такий підхід допомагає зменшити пов'язаність класів програми. Головна умова застосування патернів - "слідувати інструкції": не пхати їх абияк! Власне, давай розберемося, у яких випадках точно потрібно використовувати Bridge:-
Якщо необхідно розширити кількість сутностей на дві сторони (геометричні фігури, кольори).
-
Якщо є бажання розділити великий клас, який не відповідає принципу Single responsibility, на більш маленькі класи з вузькопрофільним функціоналом.
-
При можливій необхідності вносити зміни до логіки роботи деяких сутностей під час роботи програми.
-
За потреби сховати реалізацію від клієнтів класу (бібліотеки).
Плюси та мінуси патерну
Як і інші патерни, Мост має і переваги, і недоліки. Переваги Bridge:- Покращує масштабованість коду – можна додавати функціонал, не боячись зламати щось в іншій частині програми.
- Зменшує кількість підкласів – працює при необхідності розширення кількості сутностей у дві сторони (наприклад, кількість фігур та кількість кольорів).
- Дає можливість окремо працювати над двома самостійними гілками Абстракції та Реалізації – це можуть робити два різні розробники, не вникаючи в деталі коду один одного.
- Зменшення зв'язаності класів - єдине місце зв'язки двох класів - це міст (поле
Color color
).
- Залежно від конкретної ситуації та структури проекту в цілому, можливий негативний вплив на продуктивність програми (наприклад, якщо потрібно ініціалізувати більшу кількість об'єктів).
- Ускладнює читання коду через необхідність навігації між класами.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ