|
| 1 | +--- |
| 2 | +shortTitle: Bridge |
| 3 | +category: Structural |
| 4 | +language: de |
| 5 | +tag: |
| 6 | + - Abstraction |
| 7 | + - Decoupling |
| 8 | + - Extensibility |
| 9 | + - Gang of Four |
| 10 | + - Object composition |
| 11 | +--- |
| 12 | + |
| 13 | +## Alternativbezeichnung |
| 14 | + |
| 15 | +* Handle/Body |
| 16 | + |
| 17 | +## Zweck |
| 18 | + |
| 19 | +Das Bridge-Pattern ist ein Entwurfsmuster, das eine Abstraktion von ihrer Implementation |
| 20 | +entkoppelt, wodurch beide unabhängig voneinander variieren können. |
| 21 | +Es ist wesentlich, um flexible und erweiterbare Softwaresysteme zu bauen. |
| 22 | + |
| 23 | +## Detaillierte Erklärung |
| 24 | + |
| 25 | +Reales Beispiel |
| 26 | + |
| 27 | +> In Java wird das Bridge-Pattern oft verwendet für GUI-Frameworks, Datenbanktreiber und Gerätetreiber. |
| 28 | +> |
| 29 | +> Denken Sie an eine Universalfernbedienung (Abstraktion), die verschiedene Geräte diverser Marken |
| 30 | +> (Implementationen) schalten kann. Die Fernbedienung stellt eine einheitliche Schnittstelle bereit für |
| 31 | +> Aktionen wie Ein- und Ausschalten, Programmwechsel und Lautstärkeregelung. Diese Aktionen sind |
| 32 | +> bei jedem einzelnen Gerät unterschiedlich implementiert. Mit dem Bridge-Pattern werden diese |
| 33 | +> spezifischen Implementationen von der Fernbedienungsschnittstelle entkoppelt, sodass alle Geräte bedient |
| 34 | +> unabhängig von Marke und interner Funktionsweise bedient werden können. Diese Trennung erlaubt es, |
| 35 | +> dass neue Geräte mit der gleichen Fernbedienung ohne Änderung an deren Schnittstelle gesteuert werden können, |
| 36 | +> oder dass neue Fernbedienungen für den gleichen Gerätepool entwickelt werden können. |
| 37 | +
|
| 38 | +In einfachen Worten |
| 39 | + |
| 40 | +> Beim Bridge-Pattern geht es um den Vorrang von Komposition vor Vererbung. |
| 41 | +> Details der Implementation werden aus einer Hierarchie in ein anderes Objekt mit separater Hierarchie verschoben. |
| 42 | +
|
| 43 | +Wikipedia sagt |
| 44 | + |
| 45 | +> Das Bride-Pattern ist ein Entwurfsmuster der Softwareentwicklung mit dem Zweck, |
| 46 | +> eine Abstraktion von ihrer Implementierung zu trennen, so dass beide unabhängig voneinander angepasst werden können. |
| 47 | +
|
| 48 | +Ablaufdiagramm |
| 49 | + |
| 50 | + |
| 51 | + |
| 52 | +## Programmbeispiel |
| 53 | + |
| 54 | +Stellen Sie sich vor, eine Waffe kann diverse Verzauberungen haben und sie müssen |
| 55 | +verschiedene Waffen mit verschiedenen Verzauberungen kombinieren. Wie realisieren Sie das? |
| 56 | +Imagine you have a weapon that can have various enchantments, and you need to combine |
| 57 | +different weapons with different enchantments. How would you handle this? |
| 58 | +Durch viele Versionen einer Waffe mit jeweils einem anderen Zauber, oder würden Sie separate |
| 59 | +Verzauberungen definieren und diese nach Bedarf einer Waffe zuordnen? Das Bridge-Pattern macht letzteres möglich. |
| 60 | + |
| 61 | +Hier ist die `Weapon`-Hierarchie: |
| 62 | + |
| 63 | +```java |
| 64 | +public interface Weapon { |
| 65 | + void wield(); |
| 66 | + |
| 67 | + void swing(); |
| 68 | + |
| 69 | + void unwield(); |
| 70 | + |
| 71 | + Enchantment getEnchantment(); |
| 72 | +} |
| 73 | + |
| 74 | +public class Sword implements Weapon { |
| 75 | + |
| 76 | + private final Enchantment enchantment; |
| 77 | + |
| 78 | + public Sword(Enchantment enchantment) { |
| 79 | + this.enchantment = enchantment; |
| 80 | + } |
| 81 | + |
| 82 | + @Override |
| 83 | + public void wield() { |
| 84 | + LOGGER.info("The sword is wielded."); |
| 85 | + enchantment.onActivate(); |
| 86 | + } |
| 87 | + |
| 88 | + @Override |
| 89 | + public void swing() { |
| 90 | + LOGGER.info("The sword is swung."); |
| 91 | + enchantment.apply(); |
| 92 | + } |
| 93 | + |
| 94 | + @Override |
| 95 | + public void unwield() { |
| 96 | + LOGGER.info("The sword is unwielded."); |
| 97 | + enchantment.onDeactivate(); |
| 98 | + } |
| 99 | + |
| 100 | + @Override |
| 101 | + public Enchantment getEnchantment() { |
| 102 | + return enchantment; |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +public class Hammer implements Weapon { |
| 107 | + |
| 108 | + private final Enchantment enchantment; |
| 109 | + |
| 110 | + public Hammer(Enchantment enchantment) { |
| 111 | + this.enchantment = enchantment; |
| 112 | + } |
| 113 | + |
| 114 | + @Override |
| 115 | + public void wield() { |
| 116 | + LOGGER.info("The hammer is wielded."); |
| 117 | + enchantment.onActivate(); |
| 118 | + } |
| 119 | + |
| 120 | + @Override |
| 121 | + public void swing() { |
| 122 | + LOGGER.info("The hammer is swung."); |
| 123 | + enchantment.apply(); |
| 124 | + } |
| 125 | + |
| 126 | + @Override |
| 127 | + public void unwield() { |
| 128 | + LOGGER.info("The hammer is unwielded."); |
| 129 | + enchantment.onDeactivate(); |
| 130 | + } |
| 131 | + |
| 132 | + @Override |
| 133 | + public Enchantment getEnchantment() { |
| 134 | + return enchantment; |
| 135 | + } |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +Hier die separate `Enchantment`-Hierarchie: |
| 140 | + |
| 141 | +```java |
| 142 | +public interface Enchantment { |
| 143 | + void onActivate(); |
| 144 | + |
| 145 | + void apply(); |
| 146 | + |
| 147 | + void onDeactivate(); |
| 148 | +} |
| 149 | + |
| 150 | +public class FlyingEnchantment implements Enchantment { |
| 151 | + |
| 152 | + @Override |
| 153 | + public void onActivate() { |
| 154 | + LOGGER.info("The item begins to glow faintly."); |
| 155 | + } |
| 156 | + |
| 157 | + @Override |
| 158 | + public void apply() { |
| 159 | + LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand."); |
| 160 | + } |
| 161 | + |
| 162 | + @Override |
| 163 | + public void onDeactivate() { |
| 164 | + LOGGER.info("The item's glow fades."); |
| 165 | + } |
| 166 | +} |
| 167 | + |
| 168 | +public class SoulEatingEnchantment implements Enchantment { |
| 169 | + |
| 170 | + @Override |
| 171 | + public void onActivate() { |
| 172 | + LOGGER.info("The item spreads bloodlust."); |
| 173 | + } |
| 174 | + |
| 175 | + @Override |
| 176 | + public void apply() { |
| 177 | + LOGGER.info("The item eats the soul of enemies."); |
| 178 | + } |
| 179 | + |
| 180 | + @Override |
| 181 | + public void onDeactivate() { |
| 182 | + LOGGER.info("Bloodlust slowly disappears."); |
| 183 | + } |
| 184 | +} |
| 185 | +``` |
| 186 | + |
| 187 | +Hier werden beide Hierarchien aktiv: |
| 188 | + |
| 189 | +```java |
| 190 | +public static void main(String[] args) { |
| 191 | + LOGGER.info("The knight receives an enchanted sword."); |
| 192 | + var enchantedSword = new Sword(new SoulEatingEnchantment()); |
| 193 | + enchantedSword.wield(); |
| 194 | + enchantedSword.swing(); |
| 195 | + enchantedSword.unwield(); |
| 196 | + |
| 197 | + LOGGER.info("The valkyrie receives an enchanted hammer."); |
| 198 | + var hammer = new Hammer(new FlyingEnchantment()); |
| 199 | + hammer.wield(); |
| 200 | + hammer.swing(); |
| 201 | + hammer.unwield(); |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +Dies ist die Ausgabe: |
| 206 | + |
| 207 | +``` |
| 208 | +The knight receives an enchanted sword. |
| 209 | +The sword is wielded. |
| 210 | +The item spreads bloodlust. |
| 211 | +The sword is swung. |
| 212 | +The item eats the soul of enemies. |
| 213 | +The sword is unwielded. |
| 214 | +Bloodlust slowly disappears. |
| 215 | +The valkyrie receives an enchanted hammer. |
| 216 | +The hammer is wielded. |
| 217 | +The item begins to glow faintly. |
| 218 | +The hammer is swung. |
| 219 | +The item flies and strikes the enemies finally returning to owner's hand. |
| 220 | +The hammer is unwielded. |
| 221 | +The item's glow fades. |
| 222 | +``` |
| 223 | + |
| 224 | +## Verwendung |
| 225 | + |
| 226 | +Das Bridge-Pattern kommt in diesen Fällen in Betracht: |
| 227 | + |
| 228 | +* Eine dauerhafte Bindung zwischen Abstration und Implementierung soll vermieden werden, etwa |
| 229 | + wenn die Implementation zur Laufzeit ausgewählt oder ausgewechselt werden muss. |
| 230 | +* Sowohl Abstraktion als auch Implementationen sollen durch Vererbung erweitert werden können, |
| 231 | + um unabhängige Erweiterungen jeder Komponente zu ermöglichen. |
| 232 | +* Änderungen an der Implementation einer Abstraktion sollen die Verwender nicht beeinflussen, d.h. sie sollen nicht neu kompiliert werden müssen. |
| 233 | +* Die Hierarchie enthält eine große Anzahl Klassen, was dafür spricht, Objekte in zwei Teile zu spalten. |
| 234 | + Dieses Konzept wird von Rumbaugh als "verschachtelte Generalisierungen" bezeichnet. |
| 235 | +* Sie wollen eine Implementation mit mehreren Objekten teilen, eventuell unter Verwendung von |
| 236 | + Referenzzählerm, dabei aber dieses Detail aber vor den Verwendern verstecken. Beispiel dafür |
| 237 | + ist Copliens String-Klasse, wo mehrere Objekte die gleiche Stringdarstellung haben. |
| 238 | + |
| 239 | +## Tutorials |
| 240 | + |
| 241 | +* [Bridge Pattern Tutorial (DigitalOcean)](https://www.digitalocean.com/community/tutorials/bridge-design-pattern-java) |
| 242 | + |
| 243 | +## Reale Anwendungen in Java |
| 244 | + |
| 245 | +* In GUI-Frameworks ist das Fenster die Abstraktion, die Implementation die Fensterkonstruktion des Betriebssystem. |
| 246 | +* Bei Datenbanktreibern ist die Abstraktion eine generische Datenbankschnittstelle, die Implementationen sind datenbankspezifische Treiber. |
| 247 | +* Bei Gerätetreibern ist der geräteunabhängige Code die Abstraktion, die Implementation betrifft das einzelne Gerät. |
| 248 | + |
| 249 | +## Vor- und Nachteile |
| 250 | + |
| 251 | +Vorteile: |
| 252 | + |
| 253 | +* Entkopplung von Schnittstelle und Implementation: Durch Trennung von Operationen der höheren (Schnittstelle) und der niedrigeren Ebene (Implementation) verbessert sich die Modularität. |
| 254 | +* Bessere Erweiterbarkeit: Abstraktions- und Implementationshierarchien können unabhängig voneinander erweitert werden. |
| 255 | +* Versteckte Implementationsdetails: Verwender sehen nur die Schnittstelle, nicht ihre Implementation. |
| 256 | + |
| 257 | +Nachteile: |
| 258 | + |
| 259 | +* Höhere Komplexität: Systemarchitektur und Code können sich verkomplizieren, vor allem wenn man nicht mit dem Pattern vertraut ist. |
| 260 | +* Laufzeitkosten: Die zusätzliche Abstraktionsschicht kann zu Performanceeinbußen führen, auch wenn das in der Praxis oft vernachlässigbar ist. |
| 261 | + |
| 262 | + |
| 263 | +## Verwandte Patterns |
| 264 | + |
| 265 | +* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): |
| 266 | + Das Abstract-Factory-Pattern kann zusammen mit dem Bridge-Pattern verwendet werden, um Plattformen zu schaffen, die unabhängig von den konkreten Klassen zur Objekterzeugung sind. |
| 267 | +* [Adapter](https://java-design-patterns.com/patterns/adapter/): |
| 268 | + Das Adapter-Pattern stellt eine neue Schnittstelle für ein Objekt zur Verfügung, |
| 269 | + das Bridge-Pattern trennt die Schnittstelle des Objekts von der Implementation. |
| 270 | +* [Composite](https://java-design-patterns.com/patterns/composite/): |
| 271 | + Das Bridge-Pattern wird häufig mit dem Composite-Pattern verwendet, um die Implementationsdetails |
| 272 | + einer Komponente zu modellieren. |
| 273 | +* [Strategy](https://java-design-patterns.com/patterns/strategy/): |
| 274 | + Beide Patterns verwenden Komposition: Strategy für Veränderungen am Verhalten einer Klasse, Bridge für die Trennung von Abstraktion und Implementation. |
| 275 | + |
| 276 | +## Quellen |
| 277 | + |
| 278 | +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) |
| 279 | +* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq) |
| 280 | +* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525) |
| 281 | +* [Pattern-Oriented Software Architecture Volume 1: A System of Patterns](https://amzn.to/3TEnhtl) |
| 282 | +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) |
0 commit comments