CodeGym /Kurse /C# SELF /Abstraktionen beim Aufbau von Hierarchien

Abstraktionen beim Aufbau von Hierarchien

C# SELF
Level 22, Lektion 3
Verfügbar

1. Einführung

Lass uns mal Abstraktion aus der Vogelperspektive betrachten: Stell dir ein großes Büro vor, in dem verschiedene Mitarbeiter verschiedene Aufgaben erledigen. Sie haben vielleicht alle die gleiche Position – "Mitarbeiter", aber jeder hat seine eigene spezielle Aufgabe. Wenn du als Chef willst, dass jeder Mitarbeiter "ArbeitMachen" kann, ist es dir eigentlich egal, wie das genau passiert: Der Programmierer schreibt Code, der Buchhalter tippt Berichte. Genau diese Idee – "gemeinsamer Vertrag für alle durch Abstraktion, Details für die Spezialisten" – ist die Basis für einen sauberen Aufbau von Klassenhierarchien.

Wie sieht das im Code aus?

Alles startet mit einer abstrakten Basisklasse. Sie beschreibt das Wichtigste, was alle ihre Nachfolger können müssen.


public abstract class Mitarbeiter
{
    public string Name { get; }
    public Mitarbeiter(string name)
    {
        Name = name;
    }

    // Abstrakte Methode – Vertrag für alle Mitarbeiter
    public abstract void ArbeitMachen();
}

Danach kommen die abgeleiteten Klassen (Nachfolger) – Programmierer und Buchhalter:


public class Programmierer : Mitarbeiter
{
    public Programmierer(string name) : base(name) { }
    public override void ArbeitMachen()
    {
        Console.WriteLine($"{Name} schreibt Code");
    }
}

public class Buchhalter : Mitarbeiter
{
    public Buchhalter(string name) : base(name) { }
    public override void ArbeitMachen()
    {
        Console.WriteLine($"{Name} zählt Geld");
    }
}

Jetzt schau mal: Wir können eine Liste von Mitarbeitern erstellen – egal ob Buchhalter, Programmierer oder wen auch immer (Abstraktion hat damit überhaupt kein Problem!).


Mitarbeiter[] buero = new Mitarbeiter[]
{
    new Programmierer("Max"),
    new Buchhalter("Tanja")
};

foreach (Mitarbeiter mit in buero)
{
    mit.ArbeitMachen(); // Jeder macht seinen Job
}

Max schreibt Code
Tanja zählt Geld

Siehst du, wie schön, universell und vor allem skalierbar das geworden ist? Du kannst eine neue Klasse hinzufügen, zum Beispiel Manager, und den bestehenden Code musst du nicht ändern!

2. Wie man Hierarchien auf Basis von Abstraktionen baut

Da fast jedes OOP-Lehrbuch nicht ohne ein Tierbeispiel auskommt, machen wir das auch mal.

Abstrakte Klasse – als "Großer Diktator"


public abstract class Tier
{
    public string Name { get; set; }
    public Tier(string name)
    {
        Name = name;
    }

    // Alle Tiere müssen einen Laut machen können
    public abstract void LautMachen();

    // Aber nicht jedes muss fliegen können, deshalb:
    public virtual void Fliegen()
    {
        Console.WriteLine("Ich kann nicht fliegen.");
    }
}

Kindklassen: müssen Laut machen, können auch andere Methoden überschreiben


public class Katze : Tier
{
    public Katze(string name) : base(name) { }
    public override void LautMachen()
    {
        Console.WriteLine($"{Name}: Miau!");
    }
}

public class Hund : Tier
{
    public Hund(string name) : base(name) { }
    public override void LautMachen()
    {
        Console.WriteLine($"{Name}: Wuff!");
    }
}

public class Adler : Tier
{
    public Adler(string name) : base(name) { }
    public override void LautMachen()
    {
        Console.WriteLine($"{Name}: Schrei!");
    }

    public override void Fliegen()
    {
        Console.WriteLine($"{Name} fliegt am Himmel!");
    }
}

Und jetzt probieren wir mal, eine Sammlung aus verschiedenen Tieren zu bauen:


Tier[] zoo = new Tier[]
{
    new Katze("Felix"),
    new Hund("Rex"),
    new Adler("Eagle")
};

foreach (Tier tier in zoo)
{
    tier.LautMachen();
    tier.Fliegen();
}

Felix: Miau!
Ich kann nicht fliegen.
Rex: Wuff!
Ich kann nicht fliegen.
Eagle: Schrei!
Eagle fliegt am Himmel!

Siehst du, wie einfach wir jetzt beliebige Nachfolgerklassen nutzen können: Wir müssen nicht wissen, welches Objekt genau in der Sammlung steckt. Die Abstraktion kümmert sich um den "Vertrag", die Details um das konkrete Verhalten.

3. Abstraktion auf mehreren Ebenen

Abstraktion ist nicht nur die Aufteilung in Basisklasse und direkte Nachfolger. In komplexen Systemen gibt es oft mehrere Ebenen, wo eine abstrakte Klasse auf einer anderen aufbaut. Das ist wie ein Schichtkuchen (oder wie eine Zwiebel, würde Shrek sagen): Jede Schicht versteckt unnötige Details und lässt nur das Nötige übrig.

Beispiel: Hierarchie von Fahrzeugen


.              Fahrzeug (abstract)
            /           |           \
       Auto           Flugzeug      Boot
  ElektroAuto    JetFlugzeug    SegelBoot

Hier legt Fahrzeug die gemeinsamen Prinzipien fest (zum Beispiel die Methode Bewegen()), weiß aber nicht, wie genau ein Auto oder Flugzeug sich bewegt. Das bestimmen die abgeleiteten Klassen. Noch konkretere Klassen wie ElektroAuto oder JetFlugzeug können eigene Details hinzufügen und die Funktionalität erweitern.

Blockdiagramm der Hierarchie:


.       +------------------+
        |  Fahrzeug        |  <--- abstrakte Klasse
        +------------------+
        /        \
   +-------+   +-------+
   |  Auto |   | Boot  |  <--- Zwischenabstraktionen oder konkrete Klassen
   +-------+   +-------+

Code: abstrakte Basisklasse


public abstract class Fahrzeug
{
    public string Modell { get; }
    public Fahrzeug(string modell)
    {
        Modell = modell;
    }

    // Abstrakte Methode
    public abstract void Bewegen();
}

Zwischenabstrakte Klasse

Manchmal braucht man auch auf Zwischenebenen eigene Abstraktionen!


public abstract class Auto : Fahrzeug
{
    public Auto(string modell) : base(modell) { }

    public override void Bewegen()
    {
        Console.WriteLine($"{Modell} fährt auf der Straße.");
    }

    // Abstrakte Methode – nicht jedes Auto ist elektrisch:
    public abstract void AuftankenOderLaden();
}

Konkrete Implementierungen


public class ElektroAuto : Auto
{
    public ElektroAuto(string modell) : base(modell) { }

    public override void AuftankenOderLaden()
    {
        Console.WriteLine($"{Modell} lädt mit Strom.");
    }
}

public class BenzinAuto : Auto
{
    public BenzinAuto(string modell) : base(modell) { }

    public override void AuftankenOderLaden()
    {
        Console.WriteLine($"{Modell} tankt Benzin.");
    }
}

Ergebnis: Abstraktion auf mehreren Ebenen macht das Hinzufügen neuer Klassen und Funktionen viel einfacher.

Visualisierung: Beispiel einer Klassenhierarchie

Klasse Typ Abstrakt Eltern Besonderes Verhalten
Fahrzeug Basis Ja - Abstrakte Methode Bewegen()
Auto Zwischen Ja Fahrzeug Abstrakte AuftankenOderLaden()
ElektroAuto Ende Nein Auto Implementierung von AuftankenOderLaden()
BenzinAuto Ende Nein Auto Implementierung von AuftankenOderLaden()
Boot Ende Nein Fahrzeug Implementierung von Bewegen()

4. Einsatz von Abstraktion für skalierbare Anwendungen

Praktischer Nutzen:

  1. Flexibilität und Erweiterbarkeit des Codes: Du kannst eine neue Klasse hinzufügen (z.B. einen neuen Tier-, Fahrzeug- oder Mitarbeitertyp), ohne den bereits geschriebenen Code zu ändern. Deine Schleifen/Methoden funktionieren automatisch mit dem neuen Objekt – Hauptsache, es implementiert die in der Abstraktion definierten Methoden.
  2. Minimierung von Code-Duplikaten: Gemeinsame Eigenschaften und Methoden (z.B. Name, Basislogik) werden einmal in der abstrakten Klasse definiert und die Nachfolger bekommen sie automatisch.
  3. Weit verbreiteter Ansatz in großen Frameworks und Systemen: Zum Beispiel gibt es in ASP.NET MVC abstrakte Basiskontroller (ControllerBase), und in WinForms die abstrakte Klasse Control für alle UI-Elemente. Das macht es möglich, das Framework zu erweitern, ohne die Stabilität der bestehenden Teile zu gefährden.

Wo ist das nützlich?

  • Alle großen Systeme: Banking-Anwendungen (Mitarbeiter, Operationen und Transaktionen), Spiele (Game-Entities, Charaktere), Business-Anwendungen (Kataloge, Produkte, Nutzer), alle hierarchischen und strukturierten Daten.
  • Technische Vorstellungsgespräche: Die Fähigkeit, Klassen auf Basis von Abstraktionen zu bauen und Hierarchien zu erklären, ist eine häufige Interviewfrage.
  • Programmarchitektur: Abstraktion ermöglicht es, das "Skelett" der Architektur schon vor der Logik zu bauen – praktisch für Teamarbeit, TDD (Test-Driven Development) und einfach für Wartbarkeit.

5. Typische Fehler und Stolperfallen beim Aufbau von Hierarchien

Fehler Nr. 1: Abstrakte Methode nicht implementiert.
Der Compiler lässt dich keine Instanz einer abgeleiteten Klasse erstellen, wenn darin nicht alle abstrakten Mitglieder der Basisklasse implementiert sind. Das ist ein Muss – wie ein ungeschriebenes Gesetz: Willst du konkret sein, musst du alles implementieren.

Fehler Nr. 2: Versuch, von mehreren abstrakten Klassen zu erben.
In C# kannst du nicht von mehreren Klassen gleichzeitig erben, auch wenn sie alle abstrakt sind. Das ist eine Sprach-Einschränkung. Wenn du Verhalten von verschiedenen Quellen "erben" willst – nutze Interfaces. Die sind wie abstrakte Klassen, nur leichter und ohne Implementierung.

Fehler Nr. 3: Kein Verständnis für Zwischenabstraktionen.
Solche Klassen werden oft genutzt, um gemeinsame Logik zu gruppieren und zu verhindern, dass "unfertige" Objekte erstellt werden. Zum Beispiel kann die Klasse Auto abstrakt sein, damit niemand einfach ein "Auto" erstellt, ohne zu sagen, was für eins: Benziner, Diesel oder Elektro.

2
Aufgabe
C# SELF, Level 22, Lektion 3
Gesperrt
Erstellen einer Klassenhierarchie mit einer abstrakten Klasse
Erstellen einer Klassenhierarchie mit einer abstrakten Klasse
2
Aufgabe
C# SELF, Level 22, Lektion 3
Gesperrt
Mehrstufige Abstraktion: Transportmittel
Mehrstufige Abstraktion: Transportmittel
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION