Komposition statt Vererbung
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 41 von 70.
Vererbung erzeugt eine enge Kopplung zwischen Klassen. Wenn ein Car von Vehicle erbt, kann jede Änderung an Vehicle Car beschädigen. Komposition bietet eine flexiblere Alternative: Statt Verhalten zu erben, enthält eine Klasse Objekte, die dieses Verhalten bereitstellen.
Stell dir eine Spielfigur vor, die sich bewegen und angreifen kann. Mit Vererbung könntest du eine komplexe Hierarchie erstellen. Mit Komposition gibst du der Figur einfach die Komponenten, die sie benötigt:
public class MovementBehavior
{
public void Move() => Console.WriteLine("Moving...");
}
public class AttackBehavior
{
public void Attack() => Console.WriteLine("Attacking!");
}
public class Character
{
private MovementBehavior movement = new MovementBehavior();
private AttackBehavior attack = new AttackBehavior();
public void Move() => movement.Move();
public void Attack() => attack.Attack();
}Der entscheidende Unterschied ist die Beziehung. Vererbung stellt „ist-ein“ dar (ein Dog ist ein Animal), während Komposition „hat-ein“ darstellt (ein Character hat Bewegungsverhalten). Durch Komposition kannst du Verhaltensweisen zur Laufzeit austauschen, Fähigkeiten frei kombinieren und das fragile-Basisklassen-Problem vermeiden.
Verwende Vererbung, wenn eine echte hierarchische Beziehung besteht. Wähle Komposition, wenn du Flexibilität benötigst, wenn Verhaltensweisen von nicht verwandten Klassen gemeinsam genutzt werden könnten oder wenn du das Verhalten dynamisch ändern möchtest.
Aufgabe
EinfachErstellen wir ein Benachrichtigungssystem, das die Vorteile der Komposition gegenüber der Vererbung demonstriert. Anstatt eine komplexe Hierarchie von Benachrichtigungsklassen zu erstellen, stellst du einen NotificationSender zusammen, indem du ihm austauschbare Verhaltenskomponenten übergibst.
Du organisierst deinen Code über drei Dateien:
Behaviors.cs: Erstelle zwei Verhaltensklassen im NamespaceNotifications, die unterschiedliche Aspekte des Sendens von Benachrichtigungen behandeln:FormattingBehavior– verfügt über eine MethodeFormat(string message), die die Nachricht zurückgibt, umgeben von[ALERT]am Anfang und[END]am EndeDeliveryBehavior– verfügt über eine MethodeDeliver(string channel, string message), die eine Zeichenfolge im FormatSending via {channel}: {message}zurückgibt
NotificationSender.cs: Erstelle eine KlasseNotificationSenderim NamespaceNotifications, die Komposition verwendet, um Verhaltensweisen zu kombinieren. Dein Sender sollte:- Instanzen sowohl von
FormattingBehaviorals auch vonDeliveryBehaviorals private Felder enthalten - über eine
Channel-Eigenschaft (string) verfügen, die den Namen des Übertragungskanals speichert - über einen Konstruktor verfügen, der den Kanalnamen entgegennimmt
- über eine Methode
Send(string message)verfügen, die die Nachricht zuerst mithilfe des Formatierungsverhaltens formatiert, sie anschließend mithilfe des Übertragungsverhaltens sendet und das Endergebnis zurückgibt
- Instanzen sowohl von
Program.cs: Erstelle in deiner Hauptdatei einenNotificationSendermit einem Kanal aus der Eingabe und sende anschließend eine Nachricht aus der Eingabe. Gib das Ergebnis aus, um zu zeigen, wie die zusammengesetzten Verhaltensweisen zusammenarbeiten.
Du erhältst zwei Eingaben:
- Den Namen des Übertragungskanals (z. B.
Email,SMS) - Die zu sendende Nachricht
Gib das Ergebnis des Aufrufs von Send() für deinen Benachrichtigungssender aus.
Wenn die Eingaben beispielsweise Email und Server is down lauten, sollte die Ausgabe wie folgt aussehen:
Sending via Email: [ALERT] Server is down [END]Beachte, dass NotificationSender von nichts erbt – er „hat“ einfach Formatierungs- und Übertragungsverhalten. Dieser Kompositionsansatz bedeutet, dass du jedes der beiden Verhaltensweisen problemlos austauschen könntest, ohne die Senderklasse zu ändern, oder diese Verhaltensweisen in völlig anderen Klassen wiederverwenden könntest!
Probier es selbst
using System;
using Notifications;
class Program
{
public static void Main(string[] args)
{
// Eingabe lesen
string channel = Console.ReadLine();
string message = Console.ReadLine();
// TODO: Erstelle einen NotificationSender mit dem channel
// TODO: Sende die message und gib das Ergebnis aus
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjektinitialisiererWiederholung – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbungs-(:)-SyntaxDas „base“-Schlüsselwort„virtual“- und „override“-SchlüsselwörterVersiegelte KlassenDie „object“-BasisklasseWiederholung – Mitarbeiterhierarchie7Erweiterte Funktionen
Überladen von OperatorenIndexer (this[])ToString()-ÜberschreibungErweiterungsmethodenRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterThread-sicheres SingletonFactory-MusterObserver-Muster (Events)Strategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformKomponente für SpielfigurenFahrzeugvermietungsdienst2Eigenschaften & statische Member
Automatisch implementierte EigenschaftenNur lesbare/schreibbare EigenschaftenStatische Felder & MethodenStatische KlassenMember mit Ausdruckskörpern5Polymorphie & Interfaces
Compile- vs. Laufzeit-PolymorphieInterface vs. abstrakte KlasseMehrere InterfacesExplizite InterfacesUpcasting & DowncastingWiederholung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungGrundlagen der Dependency Injection3Klassenarchitektur
Instanz- vs. statische DatenSchlüsselwörter 'readonly' & 'const'Backing FieldsRückblick – Bankkontenverwaltung6Kapselung
ZugriffsmodifikatorenEigenschaften zur KapselungImplementierung der DatenkapselungMuster für UnveränderlichkeitRückblick – SchülerdatenÜbe selbstständig: Online-C#-Compiler