Jaki jest wzór mostka?
Wzorzec mostka jest strukturalnym wzorcem projektowym. Innymi słowy, jego głównym zadaniem jest stworzenie pełnoprawnej struktury z klas i obiektów. Most robi to, dzieląc jedną lub więcej klas na osobne hierarchie: abstrakcję i implementację . Zmiana funkcjonalności w jednej hierarchii nie pociąga za sobą zmiany w drugiej. Wszystko fajnie, ale ta definicja jest bardzo szeroka i nie odpowiada na najważniejsze pytanie: „Jaki jest wzór mostka?” Myślę, że łatwiej będzie Ci zrozumieć jego praktyczne zastosowanie. Więc od razu stwórzmy klasyczny scenariusz dla wzoru mostka. MamyShape
klasę abstrakcyjną, która reprezentuje ogólną figurę geometryczną:
-
kształt.java
public abstract class Shape { public abstract void draw(); }
Kiedy zdecydujemy się dodać kształty, takie jak trójkąty i prostokąty, sprawimy, że odziedziczą one
Shape
klasę: -
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()
będzie zależała od tego koloru. Aby mieć różne implementacje metody draw()
, musimy utworzyć klasę dla każdej kombinacji kształtu i koloru. Jeśli mamy trzy kolory, potrzebujemy sześciu klas: TriangleBlack
, TriangleGreen
, TriangleRed
, RectangleBlack
, RectangleGreen
i RectangleRed
. Sześć klas to nie taki duży problem. Ale! Jeśli musimy dodać nowy kształt lub kolor, to liczba klas rośnie wykładniczo. Jak wyjść z tej sytuacji? Przechowywanie koloru w polu i wyliczanie wszystkich opcji za pomocą instrukcji warunkowych nie jest najlepszym rozwiązaniem. Dobrym rozwiązaniem jest przeniesienie koloru do osobnego interfejsu. Ledwie powiedziane, to zrobione: stwórzmy Color
interfejs z trzema implementacjami: BlackColor
, GreenColor
i RedColor
:
-
Kolor.java:
public interface Color { void fillColor(); }
-
CzarnyKolor.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"); } }
Teraz dodajemy
Color
pole doShape
klasy. Otrzymamy jego wartość w konstruktorze. -
kształt.java:
public abstract class Shape { protected Color color; public Shape(Color color) { this.color = color; } public abstract void draw(); }
Zmiennej będziemy używać
color
wShape
implementacjach. Oznacza to, że kształty mogą teraz korzystać z funkcjonalności interfejsuColor
. -
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
jest mostem łączącym dwie oddzielne hierarchie klas.
Jak zbudować most: abstrakcja i implementacja
Spójrzmy na diagram klas, który przedstawia wzorzec pomostu: Tutaj możesz zobaczyć dwie niezależne struktury, które można modyfikować bez wpływu na ich funkcjonalność. W naszym przypadku:- Abstrakcja to
Shape
klasa - RefinedAbstraction to klasy
Triangle
iRectangle
- Implementator to
Color
interfejs - ConcreteImplementor to klasy
BlackColor
,GreenColor
iRedColor
.
Shape
jest abstrakcją — mechanizmem zarządzającym wypełnianiem kształtów różnymi kolorami, który deleguje do interfejsu Color
(Implementora). Klasy Triangle
i Rectangle
to konkretne klasy, które wykorzystują mechanizm udostępniony przez Shape
klasę. BlackColor
i GreenColor
są RedColor
konkretnymi implementacjami w Hierarchii implementacji.
Gdzie używać wzoru mostu
Ogromną zaletą korzystania z tego wzorca jest możliwość wprowadzania zmian w klasach funkcjonalnych w jednej hierarchii bez naruszania logiki drugiej. Takie podejście pomaga również zmniejszyć sprzężenie między klasami. Głównym wymaganiem podczas korzystania z tego wzorca jest „postępuj zgodnie z instrukcjami” — nie ignoruj żadnego z nich! W tym celu zastanówmy się, w jakich sytuacjach zdecydowanie powinieneś użyć wzorca mostka:-
Jeśli potrzebujesz rozszerzyć liczbę podmiotów w oparciu o kombinacje dwóch pojęć (np. kształty i kolory).
-
Jeśli chcesz podzielić dużą klasę, która nie spełnia zasady pojedynczej odpowiedzialności na mniejsze klasy, które mają wąską funkcjonalność.
-
Jeśli konieczne jest dokonanie zmian w logice niektórych podmiotów w trakcie działania programu.
-
Jeśli konieczne jest ukrycie implementacji przed klientami klasy lub biblioteki.
Plusy i minusy wzoru
Podobnie jak inne wzory, most ma zarówno zalety, jak i wady. Zalety wzoru mostka:- Poprawia skalowalność kodu — możesz dodawać funkcjonalności bez obawy, że zepsujesz coś w innej części programu.
- Zmniejsza liczbę podklas, gdy w innym przypadku liczba jednostek byłaby oparta na kombinacjach dwóch pojęć (na przykład kształtów i kolorów).
- Umożliwia oddzielną pracę na dwóch odrębnych hierarchiach — Abstrakcji i Implementacji. Dwóch różnych programistów może wprowadzać zmiany bez zagłębiania się w szczegóły swojego kodu.
- Zmniejsza sprzężenie między klasami — jedynym miejscem, w którym te dwie klasy są sprzężone, jest most (tj.
Color color
pole).
- W zależności od konkretnej sytuacji i ogólnej struktury projektu może to negatywnie wpłynąć na wydajność programu (na przykład, jeśli trzeba zainicjować więcej obiektów).
- Sprawia, że kod jest mniej czytelny ze względu na konieczność przełączania się między dwiema klasami.
GO TO FULL VERSION