Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Erstellen 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 Namespace Notifications, die unterschiedliche Aspekte des Sendens von Benachrichtigungen behandeln:
    • FormattingBehavior – verfügt über eine Methode Format(string message), die die Nachricht zurückgibt, umgeben von [ALERT] am Anfang und [END] am Ende
    • DeliveryBehavior – verfügt über eine Methode Deliver(string channel, string message), die eine Zeichenfolge im Format Sending via {channel}: {message} zurückgibt
  • NotificationSender.cs: Erstelle eine Klasse NotificationSender im Namespace Notifications, die Komposition verwendet, um Verhaltensweisen zu kombinieren. Dein Sender sollte:
    • Instanzen sowohl von FormattingBehavior als auch von DeliveryBehavior als 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
  • Program.cs: Erstelle in deiner Hauptdatei einen NotificationSender mit 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
        
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C#-Compiler