Cover | 1 |
Titel | 3 |
Impressum | 4 |
Inhaltsverzeichnis | 7 |
Danksagungen | 17 |
Teil I: Einführung | 19 |
Kapitel 1: Architektur, Performance und Spiele | 27 |
1.1 Was ist Softwarearchitektur? | 27 |
1.1.1 Was zeichnet eine gute Softwarearchitektur aus? | 28 |
1.1.2 Wie nimmt man Änderungen vor? | 28 |
1.1.3 Inwiefern hilft eine Entkopplung? | 30 |
1.2 Zu welchem Preis? | 30 |
1.3 Performance und Geschwindigkeit | 32 |
1.4 Das Gute an schlechtem Code | 33 |
1.5 Ein ausgewogenes Verhältnis finden | 34 |
1.6 Einfachheit | 35 |
1.7 Fang endlich an! | 37 |
Teil II: Design Patternsneu überdacht | 39 |
Kapitel 2: Command (Befehl) | 41 |
2.1 Eingabekonfiguration | 42 |
2.2 Regieanweisungen | 45 |
2.3 Rückgängig und Wiederholen | 47 |
2.4 Klassen ohne Funktionen? | 51 |
2.5 Siehe auch ... | 53 |
Kapitel 3: Flyweight (Fliegengewicht) | 55 |
3.1 Den Wald vor lauter Bäumen nicht sehen | 55 |
3.2 Tausend Instanzen | 58 |
3.3 Das Flyweight-Pattern | 58 |
3.4 Ein Ort, um Wurzeln zu schlagen | 59 |
3.5 Und die Performance? | 64 |
3.6 Siehe auch ... | 65 |
Kapitel 4: Observer (Beobachter) | 67 |
4.1 Erzielte Leistungen | 67 |
4.2 Funktionsweise | 69 |
4.2.1 Der Observer | 69 |
4.2.2 Das Subjekt | 70 |
4.2.3 Beobachtung der Physik-Engine | 72 |
4.3 »Das ist zu langsam!« | 73 |
4.3.1 Oder ist es doch zu schnell? | 74 |
4.4 »Zu viele dynamische Allokationen« | 74 |
4.4.1 Verkettete Observer | 75 |
4.4.2 Ein Pool von Listenknoten | 79 |
4.5 Verbleibende Schwierigkeiten | 79 |
4.5.1 Subjekte und Observer löschen | 80 |
4.5.2 Keine Sorge, der Garbage Collector erledigt das schon | 81 |
4.5.3 Was geht hier vor? | 82 |
4.6 Heutige Observer | 83 |
4.7 Zukünftige Observer | 84 |
Kapitel 5: Prototype (Prototyp) | 87 |
5.1 Das Design Pattern Prototype | 87 |
5.1.1 Wie gut funktioniert es? | 91 |
5.1.2 Spawn-Funktionen | 91 |
5.1.3 Templates | 92 |
5.1.4 First-Class-Typen | 93 |
5.2 Eine auf Prototypen beruhende Sprache | 93 |
5.2.1 Self | 93 |
5.2.2 Wie ist es gelaufen? | 96 |
5.2.3 Was ist mit JavaScript? | 97 |
5.3 Prototypen zur Datenmodellierung | 99 |
Kapitel 6: Singleton | 103 |
6.1 Das Singleton-Pattern | 103 |
6.1.1 Beschränkung einer Klasse auf eine Instanz | 103 |
6.1.2 Bereitstellung eines globalen Zugriffspunkts | 104 |
6.2 Gründe für die Verwendung | 105 |
6.3 Gründe, die Verwendung zu bereuen | 107 |
6.3.1 Singletons sind globale Variablen | 108 |
6.3.2 Das Pattern löst zwei Probleme, selbst wenn es nur eins gibt | 109 |
6.3.3 Die späte Initialisierung entzieht Ihnen die Kontrolle | 110 |
6.4 Verzicht auf Singletons | 112 |
6.4.1 Wird die Klasse überhaupt benötigt? | 112 |
6.4.2 Nur eine Instanz einer Klasse | 114 |
6.4.3 Bequemer Zugriff auf eine Instanz | 115 |
6.5 Was bleibt dem Singleton? | 118 |
Kapitel 7: State (Zustand) | 119 |
7.1 Altbekanntes | 119 |
7.2 Zustandsautomaten erledigen das | 123 |
7.3 Enumerationen und Switch-Anweisungen | 124 |
7.4 Das State-Pattern | 127 |
7.4.1 Das Interface für den Zustand | 128 |
7.4.2 Klassen für alle Zustände | 128 |
7.4.3 An den Zustand delegieren | 129 |
7.5 Wo sind die Zustandsobjekte? | 130 |
7.5.1 Statische Zustände | 130 |
7.5.2 Instanziierte Zustandsobjekte | 131 |
7.6 Eintritts- und Austrittsaktionen | 132 |
7.7 Wo ist der Haken? | 134 |
7.8 Nebenläufige Zustandsautomaten | 134 |
7.9 Hierarchische Zustandsautomaten | 136 |
7.10 Kellerautomaten | 138 |
7.11 Wie nützlich sind sie? | 139 |
Teil III: Sequenzierungsmuster (Sequencing Patterns) | 141 |
Kapitel 8: Double Buffer (Doppelter Buffer) | 143 |
8.1 Motivation | 143 |
8.1.1 Computergrafik kurz und bündig | 143 |
8.1.2 Erster Akt, erste Szene | 145 |
8.1.3 Zurück zur Grafik | 146 |
8.2 Das Pattern | 146 |
8.3 Anwendbarkeit | 147 |
8.4 Konsequenzen | 147 |
8.4.1 Der Austausch selbst kostet Zeit | 147 |
8.4.2 Zwei Framebuffer belegen mehr Arbeitsspeicher | 147 |
8.5 Beispielcode | 148 |
8.5.1 Nicht nur Grafik | 151 |
8.5.2 Künstliche Unintelligenz | 151 |
8.5.3 Gebufferte Ohrfeigen | 155 |
8.6 Designentscheidungen | 156 |
8.6.1 Wie werden die Buffer ausgetauscht? | 157 |
8.6.2 Wie fein ist der Buffer untergliedert? | 158 |
8.7 Siehe auch ... | 159 |
Kapitel 9: Game Loop (Hauptschleife) | 161 |
9.1 Motivation | 161 |
9.1.1 Interview mit einer CPU | 161 |
9.1.2 Ereignisschleifen | 162 |
9.1.3 Eine aus dem Takt geratene Welt | 163 |
9.1.4 Sekunden pro Sekunde | 164 |
9.2 Das Pattern | 164 |
9.3 Anwendbarkeit | 164 |
9.4 Konsequenzen | 165 |
9.4.1 Abstimmung mit der Ereignisschleife des Betriebssystems | 165 |
9.5 Beispielcode | 166 |
9.5.1 Die Beine in die Hand nehmen | 166 |
9.5.2 Ein kleines Nickerchen | 166 |
9.5.3 Ein kleiner und ein großer Schritt | 167 |
9.5.4 Aufholjagd | 169 |
9.5.5 In der Mitte hängen geblieben | 171 |
9.6 Designentscheidungen | 173 |
9.6.1 Stammt die Game Loop aus Ihrer Feder oder benutzen Sie die der Plattform? | 173 |
9.6.2 Wie handhaben Sie die Leistungsaufnahme? | 174 |
9.6.3 Wie steuern Sie die Spielgeschwindigkeit? | 175 |
9.7 Siehe auch ... | 176 |
Kapitel 10: Update Method (Aktualisierungsmethode) | 177 |
10.1 Motivation | 177 |
10.2 Das Pattern | 180 |
10.3 Anwendbarkeit | 180 |
10.4 Konsequenzen | 181 |
10.4.1 Verkomplizierung durch Aufteilen des Codes in einzelne Frames | 181 |
10.4.2 Der Zustand muss gespeichert werden, um im nächsten Frame fortfahren zu können | 181 |
10.4.3 Alle Objekte simulieren jeden Frame, aber nicht wirklich exakt gleichzeitig | 182 |
10.4.4 Obacht bei der Modifizierung der Objektliste während der Aktualisierung | 182 |
10.5 Beispielcode | 184 |
10.5.1 Entity-Unterklassen? | 186 |
10.5.2 Entities definieren | 186 |
10.5.3 Zeitablauf | 189 |
10.6 Designentscheidungen | 190 |
10.6.1 Zu welcher Klasse gehört die update()-Methode? | 190 |
10.6.2 Wie werden inaktive Objekte gehandhabt? | 191 |
10.7 Siehe auch ... | 192 |
Teil IV: Verhaltensmuster (Behavioral Patterns) | 193 |
Kapitel 11: Bytecode | 195 |
11.1 Motivation | 195 |
11.1.1 Wettkampf der Zaubersprüche | 196 |
11.1.2 Daten > Code | 196 |
11.1.3 Das Interpreter-Pattern | 196 |
11.1.4 Faktisch Maschinencode | 200 |
11.2 Das Pattern | 201 |
11.3 Anwendbarkeit | 201 |
11.4 Konsequenzen | 201 |
11.4.1 Befehlsformat | 202 |
11.4.2 Fehlender Debugger | 203 |
11.5 Beispielcode | 203 |
11.5.1 Eine zauberhafte API | 203 |
11.5.2 Ein bezaubernder Befehlssatz | 204 |
11.5.3 Eine Stackmaschine | 206 |
11.5.4 Verhalten = Komposition | 209 |
11.5.5 Eine virtuelle Maschine | 212 |
11.5.6 Hexerwerkzeuge | 213 |
11.6 Designentscheidungen | 215 |
11.6.1 Wie greifen Befehle auf den Stack zu? | 215 |
11.6.2 Welche Befehle gibt es? | 216 |
11.6.3 Wie werden Werte repräsentiert? | 217 |
11.6.4 Wie wird der Bytecode erzeugt? | 220 |
11.7 Siehe auch ... | 222 |
Kapitel 12: Subclass Sandbox (Unterklassen-Sandbox) | 223 |
12.1 Motivation | 223 |
12.2 Das Pattern | 225 |
12.3 Anwendbarkeit | 226 |
12.4 Konsequenzen | 226 |
12.5 Beispielcode | 226 |
12.6 Designentscheidungen | 229 |
12.6.1 Welche Operationen sollen bereitgestellt werden? | 229 |
12.6.2 Sollen Methoden direkt oder durch Objekte, die sie enthalten, bereitgestellt werden? | 231 |
12.6.3 Wie gelangt die Basisklasse an die benötigten Zustände? | 232 |
12.7 Siehe auch ... | 236 |
Kapitel 13: Type Object (Typ-Objekt) | 237 |
13.1 Motivation | 237 |
13.1.1 Die typische OOP-Lösung | 237 |
13.1.2 Eine Klasse für eine Klasse | 239 |
13.2 Das Pattern | 241 |
13.3 Anwendbarkeit | 241 |
13.4 Konsequenzen | 242 |
13.4.1 Typ-Objekte müssen manuell gehandhabt werden | 242 |
13.4.2 Die Definition des Verhaltens der verschiedenen Typen ist schwieriger | 242 |
13.5 Beispielcode | 243 |
13.5.1 Typartiges Verhalten von Typ-Objekten: Konstruktoren | 245 |
13.5.2 Gemeinsame Nutzung von Daten durch Vererbung | 246 |
13.6 Designentscheidungen | 250 |
13.6.1 Ist das Typ-Objekt gekapselt oder zugänglich? | 250 |
13.6.2 Wie werden Typ-Objekte erzeugt? | 251 |
13.6.3 Kann sich der Typ ändern? | 252 |
13.6.4 Welche Formen der Vererbung werden unterstützt? | 253 |
13.7 Siehe auch ... | 254 |
Teil V: Entkopplungsmuster (Decoupling Patterns) | 255 |
Kapitel 14: Component (Komponente) | 257 |
14.1 Motivation | 257 |
14.1.1 Der Gordische Knoten | 258 |
14.1.2 Den Knoten durchschlagen | 258 |
14.1.3 Unerledigtes | 259 |
14.1.4 Wiederverwendung | 259 |
14.2 Das Pattern | 261 |
14.3 Anwendbarkeit | 261 |
14.4 Konsequenzen | 262 |
14.5 Beispielcode | 262 |
14.5.1 Eine monolithische Klasse | 263 |
14.5.2 Abspalten eines Bereichs | 264 |
14.5.3 Abspalten der übrigen Bereiche | 266 |
14.5.4 Robo-Bjørn | 268 |
14.5.5 Ganz ohne Bjørn? | 270 |
14.6 Designentscheidungen | 272 |
14.6.1 Wie gelangt ein Objekt an seine Komponenten? | 273 |
14.6.2 Wie kommunizieren die Komponenten untereinander? | 273 |
14.7 Siehe auch ... | 277 |
Kapitel 15: Event Queue (Ereigniswarteschlange) | 279 |
15.1 Motivation | 279 |
15.1.1 Ereignisschleife der grafischen Benutzeroberfläche | 279 |
15.1.2 Zentrale Ereignissammlung | 280 |
15.1.3 Wie bitte? | 281 |
15.2 Das Pattern | 284 |
15.3 Anwendbarkeit | 284 |
15.4 Konsequenzen | 285 |
15.4.1 Eine zentrale Ereigniswarteschlange ist eine globale Variable | 285 |
15.4.2 Den Boden unter den Füßen verlieren | 285 |
15.4.3 Steckenbleiben in Rückkopplungsschleifen | 286 |
15.5 Beispielcode | 286 |
15.5.1 Ein Ring-Buffer | 289 |
15.5.2 Anfragen zusammenfassen | 293 |
15.5.3 Threads | 294 |
15.6 Designentscheidungen | 295 |
15.6.1 Was soll in die Warteschlange aufgenommen werden? | 295 |
15.6.2 Wer darf lesend auf die Warteschlange zugreifen? | 296 |
15.6.3 Wer darf schreibend auf die Warteschlange zugreifen? | 298 |
15.6.4 Wie lang ist die Lebensdauer der Objekte in der Warteschlange? | 299 |
15.7 Siehe auch ... | 300 |
Kapitel 16: Service Locator (Dienstlokalisierung) | 301 |
16.1 Motivation | 301 |
16.2 Das Pattern | 302 |
16.3 Anwendbarkeit | 302 |
16.4 Konsequenzen | 303 |
16.4.1 Der Dienst muss auch tatsächlich lokalisiert werden können | 303 |
16.4.2 Dem Dienst ist nicht bekannt, wer ihn nutzt | 303 |
16.5 Beispielcode | 304 |
16.5.1 Der Dienst | 304 |
16.5.2 Der Dienstanbieter | 304 |
16.5.3 Ein einfacher Service Locator | 305 |
16.5.4 Ein leerer Dienst | 306 |
16.5.5 Protokollierender Dekorierer | 308 |
16.6 Designentscheidungen | 310 |
16.6.1 Wie wird der Dienst lokalisiert? | 310 |
16.6.2 Was geschieht, wenn die Lokalisierung des Dienstes scheitert? | 312 |
16.6.3 Wer darf auf den Dienst zugreifen? | 315 |
16.7 Siehe auch ... | 316 |
Teil VI: Optimierungsmuster (Optimization Patterns) | 317 |
Kapitel 17: Data Locality (Datenlokalität) | 319 |
17.1 Motivation | 319 |
17.1.1 Ein Datenlager | 320 |
17.1.2 Eine Palette für die CPU | 322 |
17.1.3 Daten = Performance? | 323 |
17.2 Das Pattern | 324 |
17.3 Anwendbarkeit | 325 |
17.4 Konsequenzen | 325 |
17.5 Beispielcode | 326 |
17.5.1 Aneinandergereihte Arrays | 326 |
17.5.2 Gebündelte Daten | 331 |
17.5.3 Hot/Cold Splitting | 335 |
17.6 Designentscheidungen | 337 |
17.6.1 Wie wird Polymorphismus gehandhabt? | 338 |
17.6.2 Wie werden Spielobjekte definiert? | 339 |
17.7 Siehe auch ... | 342 |
Kapitel 18: Dirty Flag (Veraltet-Flag) | 345 |
18.1 Motivation | 345 |
18.1.1 Lokale Koordinaten und Weltkoordinaten | 346 |
18.1.2 Gecachete Weltkoordinaten | 347 |
18.1.3 Verzögerte Berechnung | 348 |
18.2 Das Pattern | 350 |
18.3 Anwendbarkeit | 350 |
18.4 Konsequenzen | 351 |
18.4.1 Nicht zu lange verzögern | 351 |
18.4.2 Das Flag bei jedem Zustandswechsel ändern | 352 |
18.4.3 Vorherige abgeleitete Daten verbleiben im Speicher | 352 |
18.5 Beispielcode | 353 |
18.5.1 Nicht-optimierte Traversierung | 354 |
18.5.2 Let’s Get Dirty | 355 |
18.6 Designentscheidungen | 358 |
18.6.1 Wann wird das Dirty Flag gelöscht? | 358 |
18.6.2 Wie feingranular ist Ihr »Dirty-Tracking«? | 359 |
18.7 Siehe auch ... | 360 |
Kapitel 19: Object Pool (Objektpool) | 361 |
19.1 Motivation | 361 |
19.1.1 Der Fluch der Fragmentierung | 361 |
19.1.2 Das Beste beider Welten | 362 |
19.2 Das Pattern | 363 |
19.3 Anwendbarkeit | 363 |
19.4 Konsequenzen | 363 |
19.4.1 Der Pool verschwendet möglicherweise Speicherplatz für ungenutzte Objekte | 363 |
19.4.2 Es steht nur eine feste Anzahl von Objekten zur Verfügung | 364 |
19.4.3 Die Objekte sind von fester Größe | 365 |
19.4.4 Wiederverwendete Objekte werden nicht automatisch zurückgesetzt | 365 |
19.4.5 Unbenutzte Objekte verbleiben im Arbeitsspeicher | 366 |
19.5 Beispielcode | 366 |
19.5.1 Eine kostenlose Liste | 369 |
19.6 Designentscheidungen | 372 |
19.6.1 Sind Objekte an den Pool gekoppelt? | 372 |
19.6.2 Wer ist für die Initialisierung der wiederverwendeten Objekte verantwortlich? | 374 |
19.7 Siehe auch ... | 376 |
Kapitel 20: Spatial Partition (Räumliche Aufteilung) | 377 |
20.1 Motivation | 377 |
20.1.1 Kampfeinheiten auf dem Schlachtfeld | 377 |
20.1.2 Schlachtreihen zeichnen | 378 |
20.2 Das Pattern | 379 |
20.3 Anwendbarkeit | 379 |
20.4 Konsequenzen | 379 |
20.5 Beispielcode | 380 |
20.5.1 Ein Bogen Millimeterpapier | 380 |
20.5.2 Ein Gitternetz verketteter Einheiten | 381 |
20.5.3 Betreten des Schlachtfeldes | 383 |
20.5.4 Klirrende Schwerter | 384 |
20.5.5 Vormarschieren | 385 |
20.5.6 Um Armeslänge | 386 |
20.6 Designentscheidungen | 390 |
20.6.1 Ist die Aufteilung hierarchisch oder gleichmäßig? | 390 |
20.6.2 Hängt die Zellengröße von der Verteilung der Objekte ab? | 391 |
20.6.3 Werden die Objekte nur in den Zellen gespeichert? | 393 |
20.7 Siehe auch ... | 394 |
Stichwortverzeichnis | 395 |