Bridge
Záměr Oddělení abstrakce od implementace (tak, aby se mohly nezávisle na sobě měnit)
Příklad č. 1 (schéma)
Příklad č. 1 (schéma)
Příklad č. 1 (schéma)
Příklad č. 1 (schéma)
Schéma
Příklad č. 1 (kód) public interface IColor { using System; namespace BridgePattern {// Helps in providing truly decoupled architecture public interface IColor { void PrintLine(int x, int y, int dx, int dy); void PrintPoint(int x, int y); } public class RedColor : IColor { public void PrintLine(int x, int y, int dx, int dy) { Console.BackgroundColor = ConsoleColor.Red; Console.WriteLine("line from ({0},{1}) to ({2},{3})", x, y, dx, dy); public void PrintPoint(int x, int y) { Console.WriteLine("point ({0},{1})", x, y); public class BlueColor : IColor { Console.BackgroundColor = ConsoleColor.Blue;
Příklad č. 1 (kód) { protected IColor color; public abstract class Shape { protected IColor color; protected Shape(IColor color) this.color = color; } public abstract void Draw();
Příklad č. 1 (kód) { public Circle(IColor color) : base(color) { } public class Circle : Shape { public Circle(IColor color) : base(color) { } public override void Draw() for (int i = 0; i < 2* Math.PI; i++) { int x = (int)Math.Sin(i); int y = (int)Math.Cos(i); color.PrintPoint(x,y); } public class Rectangle : Shape public Rectangle(IColor color) : base(color) { } color.PrintLine(0,0, 0,1); color.PrintLine(0,1, 2,1); color.PrintLine(2,1, 2,0); color.PrintLine(2,0, 0,0);
Příklad č. 1 (kód) { public static void Main(string[] args) class Program { public static void Main(string[] args) Shape s1 = new Rectangle(new RedColor()); Shape s2 = new Circle(new BlueColor()); s1.Draw(); s2.Draw(); Console.ReadKey(true); }
Příklad č. 2
Příklad č. 2
Exploze počtu tříd
Oddelění rozhraní (abstrakce) a implementace (= záměr GoF Bridge) Příklad č. 2 (+ Bridge) Oddelění rozhraní (abstrakce) a implementace (= záměr GoF Bridge)
Příklad č. 2 (+ Bridge) Abstrakce implementace Klientská část rozhraní Implementováno pomocí abstraktní implementace
Příklad č. 2 (+ Bridge)
Obecné schéma GoF Bridge
Obecné schéma GoF Bridge Abstraction definuje abstraktní rozhraní (obecné chování) objektů, obsahuje odkaz na Implementor RefinedAbstraction rozšiřuje/upravuje abstraktní rozhraní Implementor rozhraní implementací (obecně se liší libovolnou měrou od Abstraction) ConcreteImplementor konkrétní implementace rozhraní Implementor
definuje složitější operace pomocí jednodušších z Implementoru Role objektů v GoF Bridge (High-Level vs Low-Level) definuje složitější operace pomocí jednodušších z Implementoru typicky obsahuje jen primitivní operace
Možnosti implementace Implementoru Právě jeden Implementor [PIMPL] "degenerovaný Bridge" není potřeba mít abstraktního předka (Implementor) lze takto skrýt detaily implementace před klientem při změně implementace není třeba rekompilovat klientský kód (stačí přelinkovat) Dva a více Implementorů Implementor se vybere v konstruktoru Abstraction implementaci může vytvářet i Abstract Factory (preferované) implementace lze za run-time měnit (např. výběr efektivnějšího algoritmu – dle velikosti dat) Sdílení Implementorů Implementor je sdílen více objekty více objektů Abstraction má stejný objekt Implementor (C++) Reference counting (chybí GC) zrušení instance reprezentující implementaci po zrušení posledního odkazu
Připomenutí - schéma příkladu č. 2
konkrétní implementace Příklad č. 2 (kód) Příklad: multiplatformní systém pro tvorbu GUI rozhraní objektů (abstrakce - abstraktní třída) rozšířené rozhraní (konkrétní třída, dědí z Window) odkaz na Implementora class Window { private: WindowImp* imp; public: void DrawContents() = 0; void DrawRect( Point p1, Point p2) { imp = getWindowImp(); imp->Rect(p1.getX(), p1.getY(), p2.getX(), p2.getY()); } protected: WindowImp getWindowImp() { if (imp == nullptr) { imp = WindowFactory.getInstance.makeWindowImp(); } return imp; } rozhraní pro potomky konkrétní implementace class IconWindow : public Window { public: void DrawContents() { WindowImp* imp = getWindowImp(); imp->Bitmap(”icon”, new Coord(0.0), new Coord(0.0)); }
Příklad č. 2 (kód) Příklad: multiplatformní systém pro tvorbu GUI rozhraní implementací primitivní metody implementace XWindow a WinWindow implementují rozhraní WindowImp konkrétní kreslící funkce mohou obsahovat privátní položky class WindowImp { public: virtual void Rect(Coord c1, Coord c2, Coord c3, Coord c4) = 0; virtual void Text(String string, Coord c1, Coord c2) = 0; virtual void Bitmap(String string, Coord c1, Coord c2) = 0; ... } class XWindowImp : WindowImp { public: virtual void Rect( Coord c1, Coord c2, Coord c3, Coord c4) { ... } ... } class WinWindowImp : WindowImp { public: void Rect( Coord c1, Coord c2, Coord c3, Coord c4) { ... }
Proč použít Bridge? Flexibilnější návrh oddělení abstraktního obecného mechanismu od "system-dependent stuff", aj. změna návrhu abstrakce neovlivňuje implementaci (a vice versa) možnost výběru implementace za běhu nezávislý vývoj implementace znovupoužitelnost implementací (např. pro další Bridge) (C++) Skrytí implementačních detailů před uživateli idiom PImpl Pointer to implementation https://www.youtube.com/watch?v=KOMl2p49rvo
Související návrhové vzory Abstract Factory Adapter jiný účel, jiná fáze vývoje Adapter - po návrhu, zajištění spolupráce mezi nezávislými třídami Bridge - při návrhu, abstrakce mající více implementací Object Adapter Bridge