CodeGym /Kurse /JAVA 25 SELF /Erstellen von Klassenhierarchien, Beispiele aus dem echte...

Erstellen von Klassenhierarchien, Beispiele aus dem echten Leben

JAVA 25 SELF
Level 17, Lektion 3
Verfügbar

1. Was ist eine Klassenhierarchie?

In der Programmierung (und nicht nur dort) ist eine Hierarchie eine baumartige Struktur: Oben im Baum befinden sich „allgemeine“ Entitäten (Basisklassen), darunter – spezifischere (Unterklassen). In Java wird eine Klassenhierarchie mittels Vererbung aufgebaut: Jede Unterklasse kann ihrerseits Elternteil für weitere Unterklassen sein, und so weiter.

Analogie:

  • Ein Tier (Animal) ist eine allgemeine Klasse.
  • Ein Säugetier (Mammal) ist ein spezieller Fall eines Tieres.
  • Ein Hund (Dog) ist ein spezieller Fall eines Säugetiers.
  • Und ein konkreter „Buddy“ ist bereits ein Objekt der Klasse Dog.

Im Code sieht das so aus:

class Animal { }
class Mammal extends Animal { }
class Dog extends Mammal { }

Eine Klassenhierarchie erlaubt es, gemeinsame Eigenschaften und Verhalten „oben“ zu beschreiben und Details „unten“. Das macht den Code logischer und vermeidet Duplizierung.

Hierarchieschema

Animal
├── Mammal
│   ├── Dog
│   └── Cat
└── Bird
    └── Sparrow

2. Wie man eine Hierarchie aufbaut: Logik und Praxis

Allgemeines und Spezielles bestimmen

Die Grundregel: Die Basisklasse enthält das, was für alle ihre Nachkommen charakteristisch ist. Alles Einzigartige gehört in die Unterklassen.

Beispiel:

  • Alle Tiere können atmen und fressen – also sollten die Methoden breathe() und eat() in der Klasse Animal sein.
  • Nur Vögel können fliegen – daher gehört die Methode fly() in die Klasse Bird, nicht in Animal.
  • Nur Hunde können bellen – die Methode bark() gehört in die Klasse Dog.

Beispiel: Tiere

// Basisklasse
class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    void eat() {
        System.out.println(name + " isst.");
    }

    void makeSound() {
        System.out.println(name + " gibt ein Geräusch von sich.");
    }
}

// Unterklasse: Säugetier
class Mammal extends Animal {
    Mammal(String name) {
        super(name);
    }

    void feedMilk() {
        System.out.println(name + " füttert die Jungen mit Milch.");
    }
}

// Unterklasse: Hund
class Dog extends Mammal {
    Dog(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " bellt: Wau-wau!");
    }

    void wagTail() {
        System.out.println(name + " wedelt mit dem Schwanz.");
    }
}

// Unterklasse: Katze
class Cat extends Mammal {
    Cat(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " miaut: Miau!");
    }

    void purr() {
        System.out.println(name + " schnurrt.");
    }
}

// Unterklasse: Vogel
class Bird extends Animal {
    Bird(String name) {
        super(name);
    }

    void fly() {
        System.out.println(name + " fliegt.");
    }

    @Override
    void makeSound() {
        System.out.println(name + " zwitschert: Tschilp-tschilp!");
    }
}

Aufruf in main:

public class ZooDemo {
    public static void main(String[] args) {
        Dog buddy = new Dog("Buddy");
        Cat fluffy = new Cat("Fluffy");
        Bird sparrow = new Bird("Spatz");

        buddy.eat();        // Buddy isst.
        buddy.makeSound();  // Buddy bellt: Wau-wau!
        buddy.feedMilk();   // Buddy füttert die Jungen mit Milch.
        buddy.wagTail();    // Buddy wedelt mit dem Schwanz.

        fluffy.eat();         // Fluffy isst.
        fluffy.makeSound();   // Fluffy miaut: Miau!
        fluffy.feedMilk();    // Fluffy füttert die Jungen mit Milch.
        fluffy.purr();        // Fluffy schnurrt.

        sparrow.eat();       // Spatz isst.
        sparrow.makeSound(); // Spatz zwitschert: Tschilp-tschilp!
        sparrow.fly();       // Spatz fliegt.
    }
}

Visualisierung: Klassenbaum

Klasse Elternklasse Besonderheiten
Animal
Object
name, eat(), makeSound()
Mammal
Animal
feedMilk()
Dog
Mammal
makeSound(), wagTail()
Cat
Mammal
makeSound(), purr()
Bird
Animal
fly(), makeSound()

3. Weitere Beispiele aus dem Alltag

Geometrische Figuren

Klassenhierarchien eignen sich hervorragend zur Modellierung von Geometrie.

// Basisklasse
class Shape {
    void draw() {
        System.out.println("Wir zeichnen eine Figur.");
    }
}

// Kreis
class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    void draw() {
        System.out.println("Wir zeichnen einen Kreis mit Radius " + radius);
    }
}

// Rechteck
class Rectangle extends Shape {
    double width, height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    void draw() {
        System.out.println("Wir zeichnen ein Rechteck " + width + "x" + height);
    }
}

Verwendung:

public class ShapeDemo {
    public static void main(String[] args) {
        Shape s1 = new Circle(5);
        Shape s2 = new Rectangle(3, 4);

        s1.draw(); // Wir zeichnen einen Kreis mit Radius 5.0
        s2.draw(); // Wir zeichnen ein Rechteck 3.0x4.0
    }
}

Transport

class Vehicle {
    void move() {
        System.out.println("Das Fahrzeug bewegt sich.");
    }
}

class Car extends Vehicle {
    @Override
    void move() {
        System.out.println("Das Auto fährt auf der Straße.");
    }
}

class Bicycle extends Vehicle {
    @Override
    void move() {
        System.out.println("Der Radfahrer tritt in die Pedale.");
    }
}

main:

Vehicle v1 = new Car();
Vehicle v2 = new Bicycle();

v1.move(); // Das Auto fährt auf der Straße.
v2.move(); // Der Radfahrer tritt in die Pedale.

Benutzer

class User {
    String username;
    User(String username) { this.username = username; }
    void login() { System.out.println(username + " hat sich angemeldet."); }
}

class Admin extends User {
    Admin(String username) { super(username); }
    void deleteUser(String user) {
        System.out.println(username + " hat den Benutzer " + user + " gelöscht");
    }
}

class Customer extends User {
    Customer(String username) { super(username); }
    void buy() { System.out.println(username + " hat einen Kauf getätigt."); }
}

4. Nützliche Feinheiten

Übertreiben Sie es nicht mit Vererbung. Vererbung ist ein Werkzeug für „is-a“-Beziehungen. Wenn Sie sagen wollen: „Die Katze ist ein Tier“, verwenden Sie Vererbung. Wenn hingegen „Die Katze hat einen Schwanz“, verwenden Sie Komposition (has-a).

Schlecht:

class Engine { /* ... */ }
class Car extends Engine { /* Ist ein Auto ein Motor? Nein, das ist Komposition! */ }

Gut:

class Car {
    Engine engine; // Das Auto enthält einen Motor
}

Vermeiden Sie zu tiefe Hierarchien. Je mehr Ebenen – desto schwieriger sind Wartung und Verständnis des Codes. In der Regel sind 2–3 Ebenen für die meisten Aufgaben das Maximum.

Vermeiden Sie auch zu „flache“ Hierarchien. Wenn Sie 20 Klassen haben, die alle direkt von einer Basisklasse erben, sollten Sie die Architektur überdenken.

Vererbung „der Bequemlichkeit halber“. Manchmal möchte man Methoden oder Felder „stehlen“, ohne über die Logik nachzudenken. Das führt ins Chaos! Wenn class A und class B logisch nicht zusammenhängen, sollte man class B extends A nicht nur wegen ein paar Methoden machen.

Verstoß gegen das Liskovsche Substitutionsprinzip. Wenn eine Unterklasse nicht ohne Überraschungen anstelle der Oberklasse eingesetzt werden kann, ist die Hierarchie falsch aufgebaut.

Code-Duplikation. Wenn Sie sich dabei ertappen: „Oh, diese Methode kopiere ich von Klasse zu Klasse“, ist es wohl Zeit, sie in die Basisklasse auszulagern.

5. Typische Fehler beim Aufbau von Klassenhierarchien

Fehler Nr. 1: Vererbung ohne „is-a“-Logik.
Wenn Sie Vererbung nur einsetzen, um Zugriff auf Methoden oder Felder zu bekommen, und nicht, weil die Unterklasse wirklich „ist“ wie die Oberklasse, wird Ihr Code schnell zum Wirrwarr. Zum Beispiel: „Das Auto erbt den Motor“ – das ist kein is-a, sondern has-a.

Fehler Nr. 2: Einzigartige Besonderheiten der Unterklasse ignorieren.
Wenn all Ihre Unterklassen gleich aussehen und nichts Neues hinzufügen, ist die Hierarchie möglicherweise überflüssig – verwenden Sie eine einzige Klasse.

Fehler Nr. 3: Code-Duplikation in Unterklassen.
Wenn Sie dieselbe Implementierung in mehrere Unterklassen kopieren, sollten Sie sie in die Oberklasse verschieben.

Fehler Nr. 4: Zu komplexe oder zu tiefe Hierarchie.
Mehrstufige Hierarchien sind schwer zu warten und zu testen. Je einfacher – desto besser!

Fehler Nr. 5: Methodenüberschreibung ohne die Annotation @Override.
Ohne die Annotation ist es leicht, sich in der Methodensignatur zu vertun; dann wird die Methode nicht überschrieben, sondern als neu angesehen. Verwenden Sie @Override immer!

1
Aufgabe
JAVA 25 SELF, Level 17, Lektion 3
Gesperrt
Virtuelles Haustier: Bobik frisst 🐕
Virtuelles Haustier: Bobik frisst 🐕
1
Aufgabe
JAVA 25 SELF, Level 17, Lektion 3
Gesperrt
Fahrschule: Bewegung und Signale 🚦
Fahrschule: Bewegung und Signale 🚦
1
Aufgabe
JAVA 25 SELF, Level 17, Lektion 3
Gesperrt
Schulhierarchie: Wer bin ich? 🎓
Schulhierarchie: Wer bin ich? 🎓
1
Aufgabe
JAVA 25 SELF, Level 17, Lektion 3
Gesperrt
Zoologisches Lexikon: Besonderheiten von Katzen 🐈
Zoologisches Lexikon: Besonderheiten von Katzen 🐈
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION