Grundlagen der Programmierung: Entwurfsmuster

Strategie: Die Lösung

Testen Sie unsere 2012 Kurse

10 Tage kostenlos!

Jetzt testen Alle Abonnements anzeigen
Komposition als Alternative zur Vererbung – das "Strategie"-Muster zeigt, wie es geht und was es bringt.
07:01

Transkript

Das Verhalten eines Objekts befindet sich in seinen Methoden. Die Methoden einer Klasse lassen sich in Unterklassen überschreiben und Instanzen dieser Unterklasse können dann ein anderes Verhalten zeigen. Wenn eine Klasse nun mehrere Methoden deklariert und wir versuchen alle Kombinationsmöglichkeiten auf diese Weise abzubilden, dann kann das geradezu zu einer Klassenexplosion führen. Auch die Anpassbarkeit und Erweiterbarkeit einer solchen Lösung kann sehr schlecht sein. Deshalb schauen wir uns an, welche Lösungsmöglichkeit uns das Entwurfsmuster Strategie anbietet. Wir haben hier diese Klasse Auto und wir möchten das Bremsen, das Beschleunigen und das Musik spielen unabhängig voneinander variieren können. Wir machen nun folgendes. Wir lösen das, was wir ändern möchten, also das Verhalten unseres Auto-Objekts aus diesem Objekt heraus und packen es in separate Objekte. Dazu beschreiben wir zuerst für jedes dieser Verhalten eine eigene Schnittstelle. Ich beginne mit dem Beschleunigungsverhalten und nenne die Schnittstelle Motor. Ein Motor bietet nun also die Möglichkeit zu beschleunigen. Als nächstes nehme ich das Bremsverhalten und packe es in eine Schnittstelle. Bremse. Eine Bremse sollte also eine Methode bremsen anbieten um bremsen zu können. Und schließlich braucht das Auto noch die Fähigkeit Musik spielen zu können, dafür nehmen wir die dritte Schnittstelle. Ich nenne sie mal AudioSystem. Diese Schnittstellen beschreiben nun jeweils einen Teil des Verhaltens unseres Autos, den wir verändern können möchten. Nun brauchen wir natürlich noch die entsprechenden Klassen, die diese Schnittstellen anbieten. Möchten wir also zum Beispiel 6 verschiedene Möglichkeiten der Beschleunigung anbieten, dann bedeutet das, dass wir also 6 verschiedene Klassen brauchen, die das Interface Motor entsprechend unterschiedlich implementieren. Ich habe die hier einfach Motor1 bis Motor6 bezeichnet. Jedes dieser Motor-Objekte bietet also ein ganz spezifisches Beschleunigungsverhalten an. Das Gleiche machen wir jetzt mit der Bremse. Hier hätten wir gerne 5 verschiedene Möglichkeiten zu bremsen, also quasi von sehr schlechte Bremse bis supertolle Bremse. Das heißt, hier brauchen wir 5 Klassen, die die Schnittstelle Bremse implementieren. Mit dem AudioSystem verfahren wir jetzt genauso. Wenn wir 3 verschiedene AudioSysteme haben möchten, heißt das natürlich, wir brauchen 3 Klassen, die diese Schnittstelle implementieren. Auf diese Weise haben wir nun also das Verhalten unseren Autos, das wir gerne ändern können möchten, ausgelagert in separate Klassen. Damit später unser Auto-Objekt nur mit seinem Motor, seiner Bremse und seinem AudioSystem kommunizieren kann, braucht es natürlich 3 Attribute, in denen es sich merken kann, wo sich diese Objekte befinden. Beachten Sie, dass diese 3 Attribute von Typ der jeweiligen Schnittstelle sind. Das Attribut Motor ist also vom Typ der Schnittstelle Motor. Das Attribut Bremse vom Typ der Schnittstelle Bremse und das Attribut Audio vom Typ der Schnittstelle AudioSystem. Auf diese Weise können wir nämlich hier Instanzen unserer unterschiedlichen Implementierungen einstöpseln, ohne dass das Auto etwas davon wissen muss. Alles was aus Sicht des Autos interessant ist. Es hat einen Motor. Es hat eine Bremse und es hat ein AudioSystem. Welche konkreten Implementierungen dahinterstehen, ist dem Auto egal. Jetzt müssen wir nur noch irgendwie die Verdrahtung vornehmen können, also dafür sorgen, dass das Auto tatsächlich einen Motor, eine Bremse und ein AudioSystem bekommt. Dazu nehme ich das hier auf der rechten Seite wieder weg und schiebe das Auto hier nach oben, damit ich ein bisschen mehr Platz habe. Motor, Bremse und Audio sind Attribute und für diese Attribute können wir selbstverständlich Set-Methoden zur Verfügung stellen. Nun steht der Erzeugung eines konkreten Autos eigentlich nichts mehr im Wege. Wir könnten beispielsweise eine Instanz der Klasse Motor5 erzeugen und per setMotor mit dem Auto bekannt machen. Dann bauen wir uns eine Bremse2 und rufen setBremse auf und schließlich nehmen wir noch das größte der Audiosysteme und fügen das dann mit setAudio dem Auto hinzu. In diesem Fall haben wir jetzt ein Auto mit einem dicken Motor. Die Bremsen sind zwar nicht so toll, aber dafür können wir wenigstens vernünftig Musik hören. Was passiert nun, wenn wir Musik hören wollen? Wenn wir die Methode musikSpielen aufrufen, dann spielt das Auto die Musik nicht mehr selbst, sondern es delegiert den Aufruf an das AudioSystem. Das sieht dann ungefähr so aus. Das ist die Methode musikSpielen. Hier wird über das Attribut Audio auf das entsprechende AudioSystem zugegriffen und dessen Methode musikSpielen aufgerufen, die dann tatsächlich die Musik spielt. Das Auto ist nun aber nicht darauf festgelegt bis in alle Ewigkeit mit Motor5, Bremse2 und AudioSystem3 herumzufahren. Stellen wir irgendwann fest, dass ein paar bessere Bremsen zum Beispiel doch ganz gut wären, dann rufen wir einfach die entsprechende Set-Methode auf und übergeben ein neues Bremsen-Objekt. Zum Beispiel eine Instanz von Bremse4. Das heißt, wir können das Verhalten unseres Autos dynamisch zur Laufzeit verändern. Das ist also das Strategiemuster. Wir löschen das Verhalten, das wir ändern wollen raus. Definieren eine entsprechende Schnittstelle und dann können wir eine Gruppe von Objekten bauen, die alle diese Schnittstelle anbieten. Zur Laufzeit können wir daraus das Passende auswählen und in unser Objekt einstöpseln. Wie verhält es sich bei diesem Lösungsansatz nun mit der Anpassbarkeit und der Erweiterbarkeit. Wir haben vorhin gesehen bei einer Lösung, die auf Vererbung basiert, brauchen wir für 6 Beschleunigungsmöglichkeiten, 5 Bremsmöglichkeiten und 3 Musikspielmöglichkeiten insgesamt 90 verschiedene Klassen. Wie sieht das jetzt aus? Jetzt haben wir 6 Motoren. Wir haben 5 Bremssysteme, 3 AudioSysteme und dann haben wir natürlich noch das Auto selbst und das sind insgesamt 15 Klassen. Sie sehen, 15 ist kleiner als 90. Wenn wir vorher an einem unserer Beschleunigungsverhalten eine Änderung vornehmen wollten, dann mussten wir das in allen anderen Klassen, in denen dieses Verhalten vorkam, ebenfalls anpassen. Das heißt, insgesamt mussten 15 Klassen geändert werden. Möchten wir jetzt die Implementierung eines konkreten Motors ändern, dann ändern wir tatsächlich nur diesen einen Motor. Wenn wir feststellen, 6 Arten der Beschleunigung sind nicht genug. Wir brauchen eine 7. Dann hieß das vorher, 15 neue Klassen zu erstellen. Also statt 90 brauchen wir jetzt 105 Klassen. Mit dem Strategiemuster heißt das, dass wir einfach nur eine weitere Motorimplementierung benötigen. Das heißt, wir haben eine Klasse mehr als vorher. Das waren jetzt einen Menge Zahlen. Fassen wir das Ganze also noch einmal kurz zusammen. Die Vorteile des Strategiemusters. Wir können zur Laufzeit festlegen, welchen Algorithmus wir konkret benutzen möchten. Wir können diesen Algorithmus zur Laufzeit auch austauschen. Damit kann dieses Muster eine praktikable Alternative zur Vererbung seien, bei der die Beziehungen statisch zur Entwicklungszeit festgelegt werden. Schließlich ist für Anpassungen und Erweiterungen in der Regel wesentlich weniger Aufwand nötig. Ein offensichtlicher Nachteil ist natürlich, dass wir jetzt wesentlich mehr Objekte haben. Vorher gab es zur Laufzeit einfach nur ein Auto-Objekt. Jetzt haben wir ein Auto, einen Motor, eine Bremse und ein AudioSystem. Das führt natürlich auch bei jedem Methodenaufruf zu einer zusätzlichen Indirektion, weil der Aufruf an das Auto geht. Das Auto aber den Aufruf nicht selbst ausführt, sondern weitergibt an das jeweilige zuständige Objekt. Um die einzelnen Motoren oder auch die einzelnen Bremsen untereinander austauschbar zu machen, brauchen wir zusätzliche Schnittstellenbeschreibungen und schließlich ist die Erzeugung eines Auto-Objektes am Ende dann doch aufwendiger. Es genügt dann nicht mehr zu sagen, ich hätte gerne eine Instanz der Klasse schnelleres Auto mit besseren Bremsen, sondern man muss sich den Motor instanziieren. Man muss sich die Bremsen instanziieren. Man muss sich das AudioSystem instanziieren. Dann muss man ein Auto bauen und das Auto mit diesen dreien bekannt machen. Damit haben Sie nun also gesehen, wie Sie mit dem Strategiemuster das Verhalten von Objekten zur Laufzeit ändern können.

Grundlagen der Programmierung: Entwurfsmuster

Erhalten Sie einen fundierten Einstieg in das Thema Entwurfsmuster (Design Patterns) und erfahren Sie, wie Sie Entwurfsmuster in objektorientierten Programmiersprechen umsetzen.

2 Std. 49 min (33 Videos)
Derzeit sind keine Feedbacks vorhanden...
 

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!