Frühe Bindung ist schlecht für Schnittstellen | September 2007

Traditionell wird bei dem Methodenaufruf zwischen früher und später bzw. statischer und dynamischer Bindung unterschieden. Bei der späten Bindung stellt sich erst zur Laufzeit heraus, welche Methode wirklich aufgerufen wird, während bei der frühen Bindung bereits beim Kompilieren die aufzurufende Methode bekannt ist. Bei Code, der direkt in Maschinensprache übersetzt wird, wie beispielsweise C, C++ und Delphi, kann man diesen Effekt gut nachvollziehen.

Bei einer modernen objektorientierten Programmiersprache, die interpretiert wird oder auf Bytecode basiert, lässt sich aber an der Sinnhaftigkeit und Notwendigkeit zweifeln, die aufzurufende Methode bereits vorher zu kennen. Vielmehr sieht man die Objekte als eigene Autoritäten, die selbst ihr Verhalten über die Methoden steuern und fast immer in Oberklassen oder Interfaces geboxt werden. Somit ist bei einem Objekt die Klassenzugehörigkeit entscheidend und nicht mehr der deklarierte Datentyp der Variable.

Ein sehr wichtiges Prinzip bei der Objektorientierung ist das Programmieren auf Schnittstellen und nicht auf konkrete Implementierungen. Durch eine Schnittstelle soll im Prinzip nur eine Funktionalität von einer Klasse gewährleistet sein - unabhängig davon welche konkrete Klasse die Schnittstelle implementiert. Methoden einer Schnittstelle sind also eher als Nachrichten an Objekte zu verstehen und somit immer virtuell - setzen also immer eine späte Bindung voraus.

Im folgenden Beispiel wird die zweite Methode nicht als virtuell deklariert und das Interface IMyInterface wird vererbt. Das führt dann aber dazu, dass der restliche Code, also in diesem Fall die Main-Methode, durch Frühe Bindung zwei Methoden zweier verschiedener Klassen aufruft, ohne dass es (aus dem Interface) klar wird, welche Methoden welcher Klassen das sind.

Fazit: Durch Frühe Bindung und Interface-Vererbung wird also eine neu (keyword new) deklarierte Methode in der Unterklasse nicht mehr aufrufbar !

interface IMeinInterface
{
    void ErsteMethode();
    void ZweiteMethode();
}
 
class MeineKlasse : IMeinInterface
{
    public virtual void ErsteMethode()
    {
        Console.WriteLine("Oberklasse - Erste Methode");
    }
 
    public void ZweiteMethode()
    {
        Console.WriteLine("Oberklasse - Zweite Methode");
    }
}
 
class MeineUnterklasse : MeineKlasse
{
    public override void ErsteMethode()
    {
        Console.WriteLine("Unterklasse - Erste Methode");
    }
 
    public new void ZweiteMethode()
    {
        Console.WriteLine("Unterklasse - Zweite Methode");
    }
}
 
public static void Main(string[] args) {
    IMeinInterface mi = new MeineUnterklasse();
    mi.ErsteMethode(); // Unterklasse - Erste Methode
    mi.ZweiteMethode(); // Oberklasse - Zweite Methode (!!!)
}

(originally posted on 2007-09-09)

posted 2007-09-08