Das Verstehen der grunglegenden Prinzipien (z.B.: Kapselung) und Techniken (z.B.: Vererbung) der objektorientierten Programmierung bereitet normalerweise keine großen Schwierigkeiten. Damit ist es jedoch noch nicht getan. Die eigentliche Leistung des Entwicklers besteht darin, die bereitgestellten Techniken auf die richtige Art und Weise nutzbringend einzusetzen. Im folgenden werden wichtige Aspekte der objektorientierten Entwicklung erläutert, die zum Verständnis und zum Gebrauch der Entwurfsmuster wichtig sind.
Der Unterschied zwischen Klassenvererbung und Schnittstellenvererbung
Dieser Unterschied wird in verschiedenen Sprachsystemen verschieden realisiert bzw. nicht
realisiert.
Bei der Klassenvererbung erhält das Kindobjekt
alle Variablen, sowie die Methoden inclusive ihrer Implementierung.
Analog hierzu erhält das Kindobjekt bei der Schnittstellenvererbung
nur die Signaturen der Methoden,
nicht aber deren Implementierung. Dies wird auch als "Subtyping"
bezeichnet, da eine Klasse, die die abgeleitete
Schnittstelle implementiert, unter anderem vom Typ der
beerbten Schnittstelle ist, also alle ihre Anfragen (typischerweise
darüber hinaus mehr) erfüllt.
In den Mustern und in gängigen oo-Sprachsystemen, die keinen expliziten Unterschied
bei der Vererbung machen, wird die Schnittstellenvererbung durch das Erben von
abstrakten Klassen nachgebildet.
Bei der Klassenvererbung
erbt eine Unterklasse die Implementierung
und den Typ ihrer Oberklasse (s.o.). Dem Kindobjekt können neue Variablen
und Methoden hinzugefügt werden und somit ist eine Wiederverwendung
realisiert. Diese Technik der Wiederverwendung ist auch als White-Box-Wiederverwendung
bekannt, da die Struktur des Elternobjektes eingebunden wird und somit
bekannt ist.
Die Objektkomposition hingegen basiert auf der Technik, Objekte
bestehender Klassen in eine Struktur einzubeziehen (z.B. durch Aggregation
oder Referenz). Da hier nur
die Schnittstelle des Objektes bekannt ist,
wird diese Art von Wiederverwendung
auch als Black-Box-Wiederverwendung bezeichnet.
Die Klassenvererbung ist Teil der
Programmiersprache und somit einfach
zu realisieren. Die geerbten Methoden können beibehalten oder überschrieben
werden, die Variablen werden ergänzt, genutzt oder "links liegen
gelassen". Hier läßt sich schon erahnen, daß die
Klassenvererbung zu unsauberem Programmierstil verleiten kann.
Die Vorteile der Objektkomposition liegen ganz klar in der Flexibilität: Beziehungen
zwischen Objekten lassen sich (im Gegensatz zu denen zwischen Klassen)
zur Laufzeit dynamisch erzeugen und verwerfen. Weiterhin sind die beteiligten
Objekte nur durch die Schnittstelle bekannt
und somit gegen solche gleicher
Schnittstelle austauschbar, ohne daß auf die unterschiedlichen Implementierungen
Rücksicht genommen werden muß (sofern die Spezifikation erfüllt
bleibt !). Für die Nutzung der Wiederverwendung durch
Objektkomposition spricht weiterhin, daß die Klassenvererbung die Klassenhierarchie
verkompliziert. Es werden Abhängigkeiten geschaffen. Jede Veränderung
der Implementierung oder Struktur einer Vorfahr-Klasse wirkt sich auf die
erbende Klasse aus.
Natürlich hat der ganze Vergleich einen Haken: Er ist eigentlich
gar nicht notwendig,
da diese zwei Techniken beide zur Anwendung kommen müssen,
und zwar jede dann, wenn es angezeigt ist. Es muß eine Klassenhierarchie
existieren, um überhaupt erst Objekte zur Objektkomposition ableiten
zu können. Da jedoch die Erfahrung (u.a. die der "Gang Of Four") lehrt, daß zu selten der
kompliziertere Weg der Objektkomposition (hier bedarf es einer sauberen
Schnittstellendefinition und deren Einhaltung) gewählt wird, sollte
jeder Entwickler die Möglichkeiten und Konsequenzen beider Techniken kennen. Das Wissen
um den richtigen Einsatz beider Techniken
bedarf einer gewissen Erfahrung, die letzten Endes einen Experten ausmacht.
Das Durcharbeiten der Muster ist insofern auch für unerfahrene Entwickler sinnvoll,
die dadurch für die Feinheiten des oo-Entwurfes "sensibilisiert" werden.
Mit Hilfe der Delegation (von Anfragen) kann auch mittels Objektkomposition ein Verhalten
simuliert werden, welches sonst nur über Vererbung realisiert werden kann.
Um dies erklären zu können, benutze ich ein Beispiel, welches bereits ein Entwurfsmuster
gebraucht (Zustandsmuster):
In einer Software wird eine Datei mittels eines Objektes repräsentiert. Da sich der Zustand
einer Datei ändern kann (offen/geschlossen), muß sich auch das Verhalten des entsprechenden
Objektes ändern (Das Öffnen einer offenen Datei bzw. das Schließen einer geschlossenen ist
sicherlich grober Unfug !)
In dieser starren Hierarchie stellt aber die Änderung des Dateizustandes eine Hürde dar.
Ein Objekt müßte die Klasse wechseln, was nicht möglich ist. Man müßte ein Objekt der
jeweils gewünschten Klasse erzeugen, die Variablen umkopieren, etc. Und selbst dann
bekommt man in einem komplexen Entwurf eventuell noch Probleme mit existenten
Referenzen
und wird so zu weiteren Änderungen gezwungen.
Nun ist der Fortbestand eines Datei-Objektes unabhängig von seinem Zustand gesichert.
Lediglich bei Veränderungen des Zustandes sind Veränderungen in der Objektstruktur nötig.
Diese Struktur ist zur Laufzeit dynamisch und hat somit das übergeordnete Ziel des objektorientierten
Entwurfs schon erreicht.
Möglichkeiten der Wiederverwendung von Code
- Klassenvererbung und Objektkomposition -
Delegation
Eine Möglichkeit, dies in einem objektorientierten Entwurf zu implementieren, besteht darin,
Eine abstrakte Klasse "Datei" einzuführen,
von der eine konkrete Klasse
"OffeneDatei" und
eine andere "GeschlosseneDatei" abgeleitet werden. So werden die
abstrakten Methoden, wie
beispielsweise "Oeffne()" oder "Schliesse()" überschrieben und das unterschiedliche Verhalten
ist realisiert.
Der Lösungsansatz des Zustandsmusters besteht darin, ein Datei-Objekt einzuführen, welches
diejenigen Daten bzw. Operationen enthält, die kontextunabhängig sind. Weiterhin erhält
dieses Objekt eine Referenz (meist Aggregationsbeziehung)
auf ein "Zustandsobjekt", in welchem die zustandsabhängigen Operationen implementiert sind.
Die verschiedenen Zustandsklassen stammen von einer
abstrakten Klasse ab.
[Seminarübersicht]...[Entwurfsmuster nach Gamma]...[3 Wie ein Entwurfsmuster aussieht]...[Seitenanfang]