Beim Erlernen der objektorientierten Programmierung und nach der Beherrschung der Grundlagen von Objekten, Feldern und Methoden verbringt man die meiste Zeit mit der Vererbung. Vererbung bedeutet, dass wir einen Teil der Implementierung von einer Basisklasse übernehmen. Man muss lediglich eine Unterklasse einer Basisklasse erstellen, um alle nicht-privaten Felder und Methoden zu erben.
Ein Auto und ein Flugzeug sind Fahrzeuge, also ist es offensichtlich, dass diese beiden Klassen von ihrer gemeinsamen Basisklasse namens Fahrzeug erweitert werden sollten. Dies ist ein typisches akademisches Beispiel, aber bei der Entscheidung, diese Klassen mit der Vererbungsbeziehung zu verbinden, sollten wir uns einiger Konsequenzen bewusst sein.
Abb. 1 Implementierung der Vererbungsbeziehung.
In diesem Fall sind die Klassen eng miteinander verknüpft - das bedeutet, dass die Änderungen im Verhalten jeder Klasse durch Änderungen an der Basisklasse erreicht werden können Code. Dies kann sowohl ein Vorteil als auch ein Nachteil sein - es hängt davon ab, welche Art von Verhalten wir erwarten. Wenn die Vererbung zum falschen Zeitpunkt angewendet wird, kann es beim Hinzufügen einer neuen Funktion zu Implementierungsschwierigkeiten kommen, weil sie nicht in das erstellte Klassenmodell passt. Wir müssen uns entscheiden, ob wir den Code duplizieren oder unser Modell reorganisieren wollen - und das kann ein sehr zeitaufwändiger Prozess sein. Wir können den Code, der die Vererbungsbeziehung ausführt, als "offen-geschlossen" bezeichnen - das bedeutet, dass er offen für Erweiterungen, aber geschlossen für Änderungen ist. Wenn wir davon ausgehen, dass es in der Klasse Vehicle einen allgemeinen, definierten Motorbetrieb für jedes Fahrzeug gibt, müssten wir in dem Moment, in dem wir ein motorloses Fahrzeugmodell (z. B. ein Fahrrad) zu unserer Klassenhierarchie hinzufügen wollen, einige gravierende Änderungen an unseren Klassen vornehmen.
class Fahrzeug
def start_motor
end
def stop_engine
end
end
class Flugzeug < Fahrzeug
def move
start_triebwerk
...
stop_engine
end
end
Zusammensetzung
Wenn wir nur an einem Teil des Verhaltens der bestehenden Klasse interessiert sind, ist die Komposition eine gute Alternative zur Vererbung. Anstatt Unterklassen zu erstellen, die alle Verhaltensweisen erben (die, die wir brauchen, und die, die wir überhaupt nicht brauchen), können wir die Funktionen isolieren, die wir brauchen, und unsere Objekte mit Verweisen auf diese ausstatten. Auf diese Weise geben wir den Gedanken auf, dass ein Objekt eine Art von ein Basisobjekt, zugunsten der Behauptung, dass Es enthält nur einige Teile seiner Eigenschaften.
Abb. 2 Verwendung der Zusammensetzung
Mit diesem Ansatz können wir den Code, der für den Motorbetrieb verantwortlich ist, in der autonomen Klasse Engine isolieren und nur in den Klassen, die Fahrzeuge mit Motoren darstellen, darauf verweisen. Durch die Isolierung der Funktionen mit Hilfe der Komposition wird die Struktur der Fahrzeugklasse einfacher und die Kapselung der einzelnen Klassen wird verstärkt. Die einzige Möglichkeit für die Fahrzeuge, auf den Motor einzuwirken, besteht nun darin, dessen öffentliche Schnittstelle zu verwenden, da sie keine Informationen mehr über dessen Implementierung haben. Darüber hinaus wird es möglich sein, verschiedene Arten von Motoren in verschiedenen Fahrzeugen zu verwenden und sogar ihren Austausch während des Programmablaufs zu ermöglichen. Natürlich ist die Verwendung der Komposition nicht fehlerfrei - wir schaffen einen lose verbundenen Klassensatz, der leicht erweitert werden kann und offen für Änderungen ist. Aber gleichzeitig ist jede Klasse mit vielen anderen Klassen verbunden und muss Informationen über deren Schnittstellen haben.
Klasse Fahrzeug
Ende
class Motor
def start
end
def stop
end
end
class Flugzeug < Fahrzeug
def initialize
@engine = Engine.new
end
def bewegen
@engine.start
@engine.stop
end
def change_engine(new_engine)
@engine = neue_engine
end
end
Die Auswahl
Beide beschriebenen Ansätze haben Vor- und Nachteile, wie also zwischen ihnen wählen? Vererbung ist eine Spezialisierung, daher ist es am besten, sie nur bei Problemen anzuwenden, bei denen es "ist-a"-Typ-Beziehungen gibt - wir haben es also mit einer echten Hierarchie von Typen zu tun. Da die Vererbung Klassen eng miteinander verknüpft, sollten wir zunächst immer überlegen, ob wir Komposition verwenden wollen oder nicht. Komposition sollte bei Problemen angewendet werden, bei denen es "hat-a"-Typ-Beziehungen gibt - die Klasse hat also viele Teile, ist aber mehr als eine Menge von Klassen. Ein Flugzeug besteht aus Teilen, aber für sich genommen ist es mehr - es hat zusätzliche Fähigkeiten, wie zum Beispiel das Fliegen. Führt man dieses Beispiel weiter, können einzelne Teile in verschiedenen fachlichen Varianten auftreten, und dann ist es ein guter Moment, Vererbung zu verwenden.
Sowohl die Vererbung als auch die Komposition sind nur Werkzeuge, die den Programmierern zur Verfügung stehen, und die Wahl des richtigen Werkzeugs für ein bestimmtes Problem erfordert Erfahrung. Lassen Sie uns also üben und aus unseren Fehlern lernen 🙂