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 Ihnen, Verträge zu definieren, denen andere Klassen folgen müssen, aber sie dienen unterschiedlichen Zwecken und haben unterschiedliche Fähigkeiten.

Ein Interface definiert, was eine Klasse tun muss, ohne jegliche Implementierung. 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 teilweiser 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 liegen in den Vererbungsregeln und dem Verwendungszweck.

Eine class kann mehrere interfaces implementieren, aber nur von einer abstract class erben.

Verwenden Sie ein Interface, wenn nicht verwandte Klassen gemeinsames Verhalten teilen müssen (wie IFlyable für Vögel und Flugzeuge). Verwenden Sie eine abstrakte Klasse, wenn verwandte Klassen gemeinsamen Code und Zustand teilen (wie Fahrzeuge, die einen Motorstartmechanismus teilen).

Abstrakte Klassen können Konstruktoren, Felder und Zugriffsmodifikatoren für Member haben. Interfaces konnten dies traditionell nicht, obwohl das moderne C# Standardimplementierungen erlaubt.

Im Zweifelsfall bevorzugen Sie Interfaces für mehr Flexibilität – Sie können später jederzeit eine abstrakte Klasse hinzufügen, falls eine gemeinsame Implementierung notwendig wird.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Mediaplayer-System bauen, das demonstriert, wann Schnittstellen (Interfaces) gegenüber abstrakten Klassen zu bevorzugen sind. Sie werden ein Design entwerfen, bei dem nicht verwandte Geräte das Wiedergabeverhalten über ein Interface teilen, während verwandte Medientypen eine gemeinsame Implementierung über eine abstrakte Klasse teilen.

Sie werden Ihren Code über vier Dateien organisieren:

  • IPlayable.cs: Definieren Sie ein Interface namens IPlayable im Namespace Media. Dieses Interface repräsentiert den Vertrag für alles, was Medien abspielen kann – egal ob es sich um ein Telefon, einen Fernseher oder einen dedizierten Musikplayer handelt. Es sollte eine einzige Methode Play() deklarieren, die einen String zurückgibt.
  • MediaFile.cs: Definieren Sie eine abstrakte Klasse namens MediaFile im Namespace Media. Diese stellt die gemeinsame Basis für alle Mediendateien dar. Sie sollte eine Eigenschaft Title (String) und eine Eigenschaft Duration (int, repräsentiert Sekunden) haben. Fügen Sie einen Konstruktor hinzu, der beide Werte setzt. Fügen Sie eine konkrete Methode namens GetInfo() hinzu, die "{Title} ({Duration}s)" zurückgibt. Deklarieren Sie außerdem eine abstrakte Methode GetType(), die einen String zurückgibt – jeder Medientyp wird definieren, um welche Art von Datei es sich handelt.
  • AudioFile.cs: Definieren Sie eine Klasse AudioFile im Namespace Media, die von MediaFile erbt und IPlayable implementiert. Fügen Sie eine Eigenschaft Artist (String) hinzu. Der Konstruktor sollte Titel, Dauer und Künstler akzeptieren. Überschreiben Sie GetType(), um "Audio" zurückzugeben. Implementieren Sie Play(), um "Playing audio: {Title} by {Artist}" zurückzugeben.
  • Program.cs: Erstellen Sie in Ihrer Hauptdatei ein AudioFile-Objekt unter Verwendung von Eingabewerten. Demonstrieren Sie beide Aspekte der Klasse: Rufen Sie GetInfo() und GetType() auf (geerbt von der abstrakten Klasse), und rufen Sie dann Play() auf (vom Interface). Speichern Sie die Audiodatei in einer IPlayable-Variable und rufen Sie Play() erneut auf, um Interface-Polymorphie zu zeigen.

Sie erhalten drei Eingaben:

  • Songtitel
  • Dauer in Sekunden
  • Name des Künstlers

Geben Sie 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 sind, 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

Beachten Sie, wie AudioFile von beiden Ansätzen profitiert: Es erbt die gemeinsame Implementierung (GetInfo()) von der abstrakten Klasse und erfüllt gleichzeitig den IPlayable-Vertrag. Die abstrakte Klasse ermöglicht die Wiederverwendung von Code für verwandte Medientypen, während das Interface es jeder nicht verwandten Klasse ermöglicht, "abspielbar" zu sein, ohne eine Vererbungshierarchie teilen zu müssen.

Spickzettel

Ein Interface definiert einen Vertrag, dem Klassen folgen müssen, ohne jegliche Implementierung:

public interface IFlyable
{
    void Fly();
}

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

Eine abstrakte Klasse bietet sowohl einen Vertrag als auch eine gemeinsame Implementierung:

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

Wesentliche Unterschiede:

  • Eine Klasse kann mehrere Interfaces implementieren, aber nur von einer abstrakten Klasse erben
  • Verwenden Sie Interfaces, wenn nicht verwandte Klassen gemeinsames Verhalten teilen müssen
  • Verwenden Sie abstrakte Klassen, wenn verwandte Klassen gemeinsamen Code und Zustand teilen
  • Abstrakte Klassen können Konstruktoren, Felder und Zugriffsmodifikatoren haben
  • Bevorzugen Sie Interfaces für mehr Flexibilität

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 ein AudioFile unter Verwendung der Eingabewerte
        
        // 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