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 eine Car von Vehicle erbt, kann jede Änderung an Vehicle die Klasse Car beeinträchtigen. Komposition bietet eine flexiblere Alternative: Anstatt Verhalten zu erben, enthält eine Klasse Objekte, die dieses Verhalten bereitstellen.

Stellen Sie sich einen Spielcharakter vor, der sich bewegen und angreifen kann. Mit Vererbung könnten Sie eine komplexe Hierarchie erstellen. Mit Komposition geben Sie dem Charakter einfach die Komponenten, die er 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 repräsentiert "ist-ein" (ein Hund ist ein Tier), während Komposition "hat-ein" repräsentiert (ein Charakter hat ein Bewegungsverhalten). Komposition ermöglicht es Ihnen, Verhaltensweisen zur Laufzeit auszutauschen, Fähigkeiten frei zu kombinieren und das Problem der fragilen Basisklasse zu vermeiden.

Verwenden Sie Vererbung, wenn eine echte hierarchische Beziehung besteht. Wählen Sie Komposition, wenn Sie Flexibilität benötigen, wenn Verhaltensweisen über nicht verwandte Klassen hinweg geteilt werden könnten oder wenn Sie das Verhalten dynamisch ändern möchten.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Benachrichtigungssystem erstellen, das die Vorteile von Komposition gegenüber Vererbung demonstriert. Anstatt eine komplexe Hierarchie von Benachrichtigungsklassen zu erstellen, werden Sie einen NotificationSender zusammensetzen, indem Sie ihm austauschbare Verhaltenskomponenten geben.

Sie werden Ihren Code über drei Dateien organisieren:

  • Behaviors.cs: Erstellen Sie zwei Verhaltensklassen im Namespace Notifications, die verschiedene Aspekte des Sendens von Benachrichtigungen behandeln:
    • FormattingBehavior - verfügt über eine Methode Format(string message), die die Nachricht zurückgibt, umschlossen von [ALERT] am Anfang und [END] am Ende
    • DeliveryBehavior - verfügt über eine Methode Deliver(string channel, string message), die einen String im Format Sending via {channel}: {message} zurückgibt
  • NotificationSender.cs: Erstellen Sie eine Klasse NotificationSender im Namespace Notifications, die Komposition verwendet, um Verhaltensweisen zu kombinieren. Ihr Sender sollte:
    • Instanzen von sowohl FormattingBehavior als auch DeliveryBehavior als private Felder enthalten
    • Über eine Eigenschaft Channel (string) verfügen, die den Namen des Übertragungskanals speichert
    • Einen Konstruktor haben, der den Kanalnamen entgegennimmt
    • Eine Methode Send(string message) haben, die zuerst die Nachricht mit dem Formatierungsverhalten formatiert, sie dann mit dem Zustellungsverhalten unter Verwendung des Kanals zustellt und das Endergebnis zurückgibt
  • Program.cs: Erstellen Sie in Ihrer Hauptdatei einen NotificationSender mit einem Kanal aus der Eingabe und senden Sie dann eine Nachricht aus der Eingabe. Geben Sie das Ergebnis aus, um zu zeigen, wie die zusammengesetzten Verhaltensweisen zusammenarbeiten.

Sie erhalten zwei Eingaben:

  • Den Namen des Übertragungskanals (z. B. Email, SMS)
  • Die zu sendende Nachricht

Geben Sie das Ergebnis des Aufrufs von Send() auf Ihrem Benachrichtigungssender aus.

Wenn die Eingaben beispielsweise Email und Server is down sind, sollte die Ausgabe wie folgt aussehen:

Sending via Email: [ALERT] Server is down [END]

Beachten Sie, dass der NotificationSender von nichts erbt – er "hat" einfach Formatierungs- und Zustellungsverhaltensweisen. Dieser Kompositionsansatz bedeutet, dass Sie jedes Verhalten problemlos austauschen könnten, ohne die Sender-Klasse zu ändern, oder diese Verhaltensweisen in völlig anderen Klassen wiederverwenden könnten!

Spickzettel

Komposition ist eine Alternative zur Vererbung, die flexibleren Code erstellt, indem eine Klasse Objekte enthält, die Verhalten bereitstellen, anstatt Verhalten von einer Basisklasse zu erben.

Vererbung vs. Komposition:

  • Vererbung repräsentiert „ist-ein“-Beziehungen (ein Hund ist ein Tier)
  • Komposition repräsentiert „hat-ein“-Beziehungen (ein Charakter hat ein Bewegungsverhalten)

Vorteile der Komposition:

  • Verhalten zur Laufzeit austauschen
  • Fähigkeiten frei kombinieren
  • Enge Kopplung und Probleme mit fragilen Basisklassen vermeiden
  • Verhalten über nicht verwandte Klassen hinweg teilen

Beispiel mit Verhaltenskomponenten:

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();
}

Wann welcher Ansatz verwendet werden sollte:

  • Verwenden Sie Vererbung für echte hierarchische Beziehungen
  • Wählen Sie Komposition für Flexibilität, geteilte Verhaltensweisen oder dynamische Verhaltensänderungen

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 Nachricht 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