Arbeiten mit .NET: OOP/ Vererbung/ Vererbung ganz einfach

Vererbung ist eigentlich nichts Neues. Im Gegenteil, wir kennen es schon aus der Biologie. Dort erben die Kinder Eigenschaften und Fähigkeiten der Eltern.

Nichts anderes geschieht in der Programmierung. Auch hier erben die Kinder Eigenschaften und Fähigkeiten von den Eltern.

Grundlagen

Bearbeiten

Stellen wir uns einmal folgendes Problem vor: Wir wollen ein Programm schreiben, das den Fuhrpark eines Autohauses verwaltet. Dort warten jede Menge unterschiedlicher Autos auf uns, die wir mühsam einzeln in Klassen beschreiben könnten:

class Audi_A4
{
  private string m_Farbe;
  private int m_Nummer;

  public int Nummer
  {
    get { return m_Nummer; }
    set { m_Nummer = value; }
  }

  public string Farbe
  {
    get { return m_Farbe; }
    set { m_Farbe = value; }
  }

  public void StarteMotor() {}
  public void Beschleunige() {}
  public void Hupe() {}
  public void Blinke(bool links, bool rechts) {}   
}
class Audi_A6
{
  private string m_Farbe;
  private int m_Nummer;

  public int Nummer
  {
    get { return m_Nummer; }
    set { m_Nummer = value; }
  }

  public string Farbe
  {
    get { return m_Farbe; }
    set { m_Farbe = value; }
  }

  public void StarteMotor() {}
  public void Beschleunige() {}
  public void Hupe() {}
  public void Blinke(bool links, bool rechts) {}   
}
class Audi_A8
{
  private string m_Farbe;
  private int m_Nummer;

  public int Nummer
  {
    get { return m_Nummer; }
    set { m_Nummer = value; }
  }

  public string Farbe
  {
    get { return m_Farbe; }
    set { m_Farbe = value; }
  }

  public void StarteMotor() {}
  public void Beschleunige() {}
  public void Hupe() {}
  public void Blinke(bool links, bool rechts) {}   
}

Würden wir so fortfahren, bräuchten wir Jahre. Programmierung, und gerade die objektorientierte Programmierung, soll doch aber die Arbeit leichter machen und nicht mit ständigen Copy&Paste-Orgien nerven. --- Und genau an dieser Stelle haben wir wieder einen wichtigen Punkt in der objektorientierten Programmierung eingefangen:

 

Merke: Wenn du Quelltext kopierst, hast du es meistens nicht richtig beschrieben.


Erste Abstraktion

Bearbeiten

Schauen wir uns die drei Audi-Klassen oben an, stellen wir fest, dass sie viele Dinge gemeinsam haben. Warum also nicht diese Informationen an einer Stelle zusammenfassen?

class Audi
{
  private string m_Farbe;
  private int m_Nummer;

  public int Nummer
  {
    get { return m_Nummer; }
    set { m_Nummer = value; }
  }
  public string Farbe
  {
    get { return m_Farbe; }
    set { m_Farbe = value; }
  }
 
  public void StarteMotor() {}
  public void Beschleunige() {}
  public void Hupe() {}
  public void Blinke(bool links, bool rechts) {}   
}

Das war einfach. Jetzt ist es schön zentral und wir haben viel weniger Arbeit:

class BMW
{
  private string m_Farbe;
  private int m_Nummer;

  public int Nummer
  {
    get { return m_Nummer; }
    set { m_Nummer = value; }
  }
  public string Farbe
  {
    get { return m_Farbe; }
    set { m_Farbe = value; }
  }
 
  public void StarteMotor() {}
  public void Beschleunige() {}
  public void Hupe() {}
  public void Blinke(bool links, bool rechts) {}   
}
class Porsche
{
  private string m_Farbe;
  private int m_Nummer;

  public int Nummer
  {
    get { return m_Nummer; }
    set { m_Nummer = value; }
  }
  public string Farbe
  {
    get { return m_Farbe; }
    set { m_Farbe = value; }
  }
 
  public void StarteMotor() {}
  public void Beschleunige() {}
  public void Hupe() {}
  public void Blinke(bool links, bool rechts) {}   
}

Wirklich? Wir kopieren doch schon wieder! Also müsste man es noch weiter zusammenfassen können, wenn der Merksatz stimmt, oder?

Noch mehr Abstraktion

Bearbeiten

Und wirklich: Man kann es noch weiter zusammenfassen. Audi, BMW und Porsche ist es gemeinsam, Autos zu sein, nicht wahr? Also schreiben wir eine neue Klasse Auto (oder wir nehmen einfach die Auto-Klasse, die wir in Objekte und Klassen, der erste Kontakt schon geschrieben haben):

class Auto
{
  protected int m_Nummer;
  protected string m_Farbe;
  protected string m_Typ;

  public Auto(int nummer, string farbe, string typ)
  {
    m_Nummer = nummer;
    m_Farbe = farbe;
    m_Typ = typ;
  }

  public int Nummer
  {
    get { return m_Nummer; }
    set { m_Nummer = value; }
  }

  public string Farbe
  {
    get { return m_Farbe; }
    set { m_Farbe = value; }
  }
 
  public string Typ
  {
    get { return m_Typ; }
  }

  public void StarteMotor() {}
  public void Beschleunige() {}
  public void Hupe() {}

  public void Blinke(bool links, bool rechts)
  {
    // Sollen wir links blinken?
    if (links && !rechts)
    {
      Console.WriteLine("Links blinken");
    } 
   
    // Sollen wir rechts blinken?
    if (!links && rechts)
    {
      Console.WriteLine("Rechts blinken");
    }

    // Oder ist der Warnblinker an?
    if (links && rechts)
    {
      Console.WriteLine("Der Warnblinker ist an.");
    }
  } 
}

Und nun? Jetzt haben wir das Auto wunderschön beschrieben, aber weit und breit ist kein Audi, BMW oder Porsche mehr in Sicht.

Vererbung

Bearbeiten

Genau hier setzt die Vererbung ein. Anstatt mühselig all die Eigenschaften und Fähigkeiten in jede Klasse zu schreiben, vererben wir sie einfach an die Kinder. C# nutzt dafür eine spezielle Syntax:

class Kind : Elternklasse { }

In unserem Beispiel erbt das Kind Audi also vom Vater Auto:

class Audi : Auto 
{ 
  public Audi(int nummer, string farbe) : base(nummer, farbe, "Audi") { }
}

class BMW : Auto 
{
  public BMW(int nummer, string farbe) : base(nummer, farbe, "BMW") { } 
}
 
class Porsche : Auto 
{ 
  public Porsche(int nummer, string farbe) : base(nummer, farbe, "Porsche") { }
}

Mit dem Schlüsselwort base übergeben wir die Eigenschaften, die an den Konstruktor der Kind-Klasse übergeben werden, an die Eltern-Klasse. Das ging schnell, nicht wahr? Ruck-zuck können wir so einen ganzen Park an Autos beschreiben. Aber funktioniert das wirklich? Probieren wir es aus:

// Wir holen uns ein Objekt von Audi
Audi audi = new Audi(1, "Grün");

// Ausgabe = "Audi"
Console.WriteLine(audi.Typ);

Das klappt also ganz einfach. Auf diese Weise können wir auch fantastische Vererbungslinien aufbauen. Beispielsweise können wir jetzt in den Audi-, BMW- und Porsche-Klassen jeweils nur für diese Typen gültige Eigenschaften und Fähigkeiten beschreiben.

class A4 : Audi
{
  public A4(int nummer, string farbe) : base(nummer, farbe) 
  {
    m_Typ += " A4";
  }
}

Schauen wir uns ein Objekt von der Klasse A4 an:

A4 audiA4 = new A4(1, "Rot");

// Ausgabe: "Audi A4"
Console.WriteLine(audiA4.Typ);

Mit dieser einen Zeile haben wir einen riesigen Berg Arbeit erledigt. Bei der Erstellung des Objekts audiA4 geschieht folgendes:

  • Das neue Objekt audiA4 ruft zuerst, also noch bevor es richtig "geboren" ist, den Konstruktor der Eltern-Klasse Audi auf.
    • Von dort wird der Konstruktor der Großeltern-Klasse Auto aufgerufen.
    • Im Konstruktor der Klasse Auto werden folgende Werte zugewiesen:
      • m_Nummer bekommt den Wert, der beim Erzeugen des Objekts audiA4 übergeben und bis dorthin durchgereicht wurde.
      • m_Farbe bekommt ebenfalls den Wert, der beim Erzeugen des Objekts audiA4 übergeben und bis dorthin durchgereicht wurde.
      • m_Typ bekommt zunächst den Wert "Audi", der vom Konstruktor der Klasse Audi zusätzlich übergeben wurde.
    • Da ansonsten im Konstruktor des Opas Auto nichts zu tun ist, wird der Konstruktor des Vaters Audi ausgeführt.
  • Da auch dort nichts zu tun ist, wird schließlich der Konstruktor des Kinds A4 ausgeführt.
  • Dort steht, dass der Typ m_Typ, der in diesem Augenblick noch den Text "Audi" hatte, um die Zeichenkette " A4" erweitert werden soll. Anschließend steht dort also "Audi A4" drin.


Eine Menge Arbeit, die wir mit einer einzigen Zeile angestoßen haben, oder? Vererbung macht's möglich. Mit Hilfe der Vererbung können wir also Eigenschaften und Fähigkeiten dort beschreiben, wo sie zum ersten Mal gebraucht werden, und diese anschließend an beliebig viele Kinder vererben.

Und natürlich können wir all diese geerbten Eigenschaften und Fähigkeiten auch benutzen.

A4 audiA4 = new A4(1, "Rot");

// Ausgabe = "Der Warnblinker ist an."
audiA4.Blinke(true, true);
Wikipedia hat einen Artikel zum Thema:
Wikipedia hat einen Artikel zum Thema:
Wikipedia hat einen Artikel zum Thema:

Generalisierung und Spezialisierung

Bearbeiten

Daraus ergeben sich nun auch ein paar fundamentale Begriffe der objektorientierten Programmierung.
Je weiter wir in der Erbschaftslinie A4->Audi->Auto zurückgehen, desto allgemeiner wird die Beschreibung der Eigenschaften und Fähigkeiten; Profis nennen das Generalisierung. Umgekehrt, also vom Auto bis zum A4, werden die Eigenschaften und Fähigkeiten in der Erbschaftslinie immer spezieller und präziser, je weiter wir in der Erbschaftslinie nach unten gehen. Das nennt man Spezialisierung.

Die Elternklassen nennt man auch Basisklassen; die Erben heißen Subklassen. Unsere Klasse A4 ist demzufolge die Subklasse von der Basisklasse Audi. Gleichzeitig ist die Klasse Audi die Subklasse der Basisklasse Auto.