Menu
Coddy logo textTech

Interface vs. abstrakte Klasse

Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 26 von 70.

Sowohl Interfaces als auch abstrakte Klassen ermöglichen es dir, Verträge zu definieren, die andere Klassen befolgen müssen, aber sie dienen unterschiedlichen Zwecken und verfügen über unterschiedliche Fähigkeiten.

Ein Interface definiert, was eine Klasse tun muss, ohne eine Implementierung vorzugeben. Es ist ein reiner Vertrag:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

Eine abstrakte Klasse kann sowohl einen Vertrag als auch eine gemeinsame Implementierung bereitstellen. Sie ist ein unvollständiger Bauplan:

public abstract class Vehicle
{
    public string Brand { get; set; }
    
    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
    
    public abstract void Drive();  // Muss implementiert werden
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving on roads");
    }
}

Die wesentlichen Unterschiede ergeben sich aus den Vererbungsregeln und dem Zweck.

Eine Klasse kann mehrere Schnittstellen implementieren, aber nur von einer abstrakten Klasse erben.

Verwende eine Schnittstelle, wenn unabhängige Klassen Verhalten gemeinsam nutzen müssen (wie IFlyable für Vögel und Flugzeuge). Verwende eine abstrakte Klasse, wenn verwandte Klassen gemeinsamen Code und Zustand teilen (wie Fahrzeuge, die einen gemeinsamen Mechanismus zum Starten des Motors verwenden).

Abstrakte Klassen können Konstruktoren, Felder und Zugriffsmodifizierer für Member enthalten. Schnittstellen konnten dies traditionell nicht, obwohl modernes C# Standardimplementierungen erlaubt.

Im Zweifelsfall solltest du aus Gründen der Flexibilität Schnittstellen bevorzugen – du kannst später jederzeit eine abstrakte Klasse hinzufügen, falls eine gemeinsame Implementierung erforderlich wird.

challenge icon

Aufgabe

Einfach

Erstelle ein Mediaplayer-System, das veranschaulicht, wann Schnittstellen und wann abstrakte Klassen verwendet werden. Du entwirfst ein System, in dem nicht verwandte Geräte über eine Schnittstelle Wiedergabeverhalten teilen, während verwandte Medientypen über eine abstrakte Klasse eine gemeinsame Implementierung nutzen.

Du organisierst deinen Code auf vier Dateien:

  • IPlayable.cs: Definiere eine Schnittstelle namens IPlayable im Namespace Media. Diese Schnittstelle stellt den Vertrag für alles dar, was Medien abspielen kann – unabhängig davon, ob es sich um ein Telefon, einen Fernseher oder einen dedizierten Musikplayer handelt. Sie soll eine einzige Methode Play() deklarieren, die einen String zurückgibt.
  • MediaFile.cs: Definiere eine abstrakte Klasse namens MediaFile im Namespace Media. Sie stellt die gemeinsame Grundlage für alle Mediendateien dar. Sie soll eine Eigenschaft Title (string) und eine Eigenschaft Duration (int, die Sekunden repräsentiert) besitzen. Füge einen Konstruktor hinzu, der beide Werte setzt. Füge außerdem eine konkrete Methode namens GetInfo() hinzu, die "{Title} ({Duration}s)" zurückgibt. Deklariere außerdem eine abstrakte Methode GetType(), die einen String zurückgibt – jeder Medientyp legt fest, um welche Art von Datei es sich handelt.
  • AudioFile.cs: Definiere eine Klasse AudioFile im Namespace Media, die von MediaFile erbt und IPlayable implementiert. Füge eine Eigenschaft Artist (string) hinzu. Der Konstruktor soll Titel, Dauer und Interpret entgegennehmen. Überschreibe GetType(), sodass "Audio" zurückgegeben wird. Implementiere Play(), sodass "Playing audio: {Title} by {Artist}" zurückgegeben wird.
  • Program.cs: Erstelle in deiner Hauptdatei anhand von Eingabewerten ein AudioFile. Veranschauliche beide Aspekte der Klasse: Rufe GetInfo() und GetType() auf (von der abstrakten Klasse geerbt), und rufe anschließend Play() auf (aus der Schnittstelle). Speichere die Audiodatei in einer IPlayable-Variable und rufe Play() erneut auf, um den Polymorphismus der Schnittstelle zu zeigen.

Du erhältst drei Eingaben:

  • Songtitel
  • Dauer in Sekunden
  • Name des Interpreten

Gib die Ausgabe in diesem Format aus:

Info: {GetInfo() result}
Type: {GetType() result}
Direct: {Play() result}
Via Interface: {Play() result from IPlayable variable}

Wenn die Eingaben beispielsweise Bohemian Rhapsody, 354 und Queen lauten, sollte die Ausgabe wie folgt aussehen:

Info: Bohemian Rhapsody (354s)
Type: Audio
Direct: Playing audio: Bohemian Rhapsody by Queen
Via Interface: Playing audio: Bohemian Rhapsody by Queen

Beachte, wie AudioFile von beiden Ansätzen profitiert: Es erbt eine gemeinsame Implementierung (GetInfo()) von der abstrakten Klasse und erfüllt gleichzeitig den Vertrag von IPlayable. Die abstrakte Klasse ermöglicht die Wiederverwendung von Code für verwandte Medientypen, während die Schnittstelle es jeder nicht verwandten Klasse ermöglicht, „abspielbar“ zu sein, ohne eine gemeinsame Vererbungshierarchie zu benötigen.

Probier es selbst

using System;
using Media;

class Program
{
    public static void Main(string[] args)
    {
        // Eingabewerte lesen
        string title = Console.ReadLine();
        int duration = Convert.ToInt32(Console.ReadLine());
        string artist = Console.ReadLine();
        
        // TODO: Erstelle eine AudioFile mit den Eingabewerten
        
        // TODO: Gib Info: gefolgt vom Ergebnis von GetInfo() aus
        
        // TODO: Gib Type: gefolgt vom Ergebnis von GetType() aus
        
        // TODO: Gib Direct: gefolgt vom Ergebnis von Play() aus
        
        // TODO: Speichere die Audiodatei in einer IPlayable-Variable
        
        // TODO: Gib Via Interface: gefolgt vom Ergebnis von Play() aus der IPlayable-Variable 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