Arbeiten mit .NET: OOP/ Vererbung/ Versiegelte Klassen
In der Vererbung von Klassen gibt es einige Besonderheiten, mit denen wir spezielle Gegebenheiten erheblich vereinfachen können.
Problem
[Bearbeiten]Wir sind immer noch mit unserer Fuhrparkverwaltung beschäftigt, und haben nach dem bisherigen Aufbau unseres kleinen Fuhrpark-Frameworks bei den Audis folgende Struktur:
abstract class Auto { } abstract class Audi : Auto { } class A4 : Audi { }
Natürlich hat das Autohaus noch alte Audi 80 rumstehen. Also müssen wir die auch mit aufnehmen:
class Audi80 : Audi { }
Und prompt haben wir ein Problem. Wir können diese Klasse (class Audi80) weitervererben, obwohl Audi keinen echten Erben mehr gebaut hat. Der Audi A4 hat so viele technische Änderungen erfahren, dass er aus logischer Sicht direkt von der Klasse Audi erben muss. Also würden wir gern ausschließen, dass jemand versehentlich von der Klasse Audi80 erbt. Und auch das können wir - wir sind mittlerweile wohl nicht mehr überrascht - ganz einfach machen. Wir nehmen dazu:
Versiegelte Klassen
[Bearbeiten]Versiegelte Klassen bedeuten schlicht und einfach das endgültige Ende der Erbschaftslinie in dieser Richtung. In der Biologie würde man wohl sagen: Unfruchtbar!
sealed class Audi80 : Audi { }
Und weil diese Unfruchtbarkeit bedeutet, dass es keine Erben gibt, können wir es nicht gleichzeitig mit dem Schlüsselwort abstract verwenden. Was sollte denn auch eine Klasse für einen Sinn haben, von der wir weder Objekte noch Erben erzeugen können, nicht wahr?
Und das war's auch schon. Naja - fast. Denn neben versiegelten Klassen gibt es auch noch
Versiegelte Methoden und versiegelte Eigenschaften
[Bearbeiten]Die Verwendung versiegelter Methoden und Eigenschaften ist allerdings an ziemlich viele Bedingungen und sehr tiefe Kenntnisse der Vererbungslehre geknüpft, dass wir sie an dieser Stelle einfach ignorieren.
Weil wir aber neugierig sind, eine kurze Erklärung: Das Schlüsselwort abstract hat im C# noch eine implizite - also eine zusätzliche, aber nicht extra erwähnte - Bedeutung: Es macht die so deklarierten Methoden und Eigenschaften gleichzeitig virtuell. Dieses virtuell (engl. virtual) wiederum zeigt an, dass sie "überschreibbar" (engl. overridable) sind, also im Laufe der Vererbung inhaltlich vollständig geändert werden können.
Das Schlüsselwort sealed wiederum hebt diese "Überschreibbarkeit" im Laufe der weiteren Erbschaftsfolge auf, vorausgesetzt, die Methode war vorher als virtual oder abstract gekennzeichnet.
Das klingt wahnsinnig kompliziert und eindrucksvoll, ist es aber nicht wirklich. Objektorientierte Programmierung hat sich zum Ziel gesetzt, das Leben abzubilden. Werfen wir also einen Blick auf das Leben da draußen:
Von unseren Eltern haben wir bestimmte Eigenschaften geerbt, die sich aber bei uns gänzlich anders entwickeln können, als sie es bei unseren Eltern taten. Wenn ich so auf meinen Schreibtisch schaue, dann fällt mir prompt die Eigenschaft "Ordnungswille" ins Auge. Grundsätzlich handelt es sich dabei um eine biologische Grundfertigkeit; einen gewissen Hang zur Ordnung haben wir alle --- wir interpretieren ihn nur unterschiedlich.
Während mein Vater dies aber bis zur Perfektion ausgebaut hat, habe ich diese Anlagen (und die jahrelange Erziehung) völlig anders interpretiert; ich habe sie "überschrieben". Also war die biologische Voraussetzung, Ordnung halten zu wollen, wohl abstract. Mein Vater hat in die Methode HalteOrdnung() seine Interpretation geschrieben, in der Hoffnung, dass ich sie erbe. Ich jedoch habe das "Überschreiben" (Schlüsselwort override) genutzt, um meinerseits eine völlig andere Interpretation hineinzuschreiben. Hätte mein Vater die Möglichkeit gehabt, die Methode HalteOrdnung() zu versiegeln, wäre aus mir ein ordentlicher Mensch geworden, denn dann hätte ich es nicht mehr überschreiben können und damit leben müssen, was mein Vater mir vererbte. Er hätte also die biologische - und abstrakte - Grunddisposition
public abstract void HalteOrdnung();
mit seiner Interpretation überschrieben und sie zugleich versiegelt
public override sealed void HalteOrdnung() { // Meines Vaters Interpretation... }
Dann hätte ich keine Chance mehr, zu versuchen, sie meinerseits ebenfalls zu überschreiben:
// Fehler: Kann nicht mehr überschrieben werden. public override void HalteOrdnung() { // Meine Interpretation... }
Und?! War das jetzt kompliziert? Nicht wirklich, oder? Trotzdem müssen wir sehr sorgfältig darauf achten, wann und wo wir Methoden versiegeln, denn dabei kann es schnell zu ungewollten Seiteneffekten kommen, wenn wir die Vererbung von Aktionen in Methoden erzwingen, die wir später vielleicht so gar nicht mehr wollen.
Siehe auch
[Bearbeiten]MSDN C#-Programmierreferenz: sealed (C#)
MSDN C#-Programmierhandbuch Abstrakte und versiegelte Klassen und Klassenmember