Das Kinderzimmer im Code:
Polymorphismen in der Programmierung

Was sind Überschreibungen, Überladungen und Polyfills – und wozu benötigt man diese überhaupt?

Wer (mindestens) ein kleines Kind zu Hause hat, kennt sicher das folgende Phänomen: An einem Tag steht die Spielküche noch in der Ecke hinten links, aber schon am Folgetag hat der Nachwuchs das gesamte Zimmer umgeräumt, so dass nicht nur die Spielküche in eine andere Ecke bugsiert wurde, sondern nahezu jedes nur irgendwie bewegliche Möbelstück seine Position verändert hat.

Auch wenn mir wohl jeder zusatimmen wird, dass sich das Erscheinungsbild des Zimmers eindeutig verändert hat, ist es immer noch als Kinderzimmer zu erkennen. Damit unterliegt Das Zimmer einem Effekt, den man in der Programmierung Polymorphismus nennt.

Zugegeben, dieser Vergleich hinkt ein Wenig (wie wohl die meisten Vergleiche), denn eigentlich müsste ergänzt werden „während Sie das Zimmer am ersten Tag noch durch die Tür betreten haben, mussten Sie am Folgetag durch das Fenster hereinklettern“ oder „obwohl mithilfe einer Trockenbauwand das Zimmer um einige Quadratmeter verkleinert wurde, hat die Spielküche noch hineingepasst“, doch das Grundkonzept sollte klar sein: Polymorphismen treten in der objektorientierten Programmierung immer dort auf wo zur Laufzeit ähnliche Funktionen unter leicht varrierenden Bedingungen benötigt werden oder Methoden an Laufzeitbedingungen angepasst werden müssen. Dazu gehören z.B.:

  • Unterschiedliche Interpreter oder Laufzeitumgebungen, in denen der gleiche Code (z.B. ein Skript) ausgeführt werden soll – z.B. unterschiedliche Webbrowser, in denen ein-und-dieselbe HTML-Website gerendert werden soll
  • Aufruf von Methoden mit Übergabe unterschiedlicher Argumente, deren vorherige Umwandlung zu aufwändig oder schlicht nicht möglich wären
  • Methoden, die zur Laufzeit unterschiedliche Werte(-Typen) zurückgeben müssen, die evtl. auch nicht ohne Mehraufwände ineinander umgewandelt werden können
  • Vererbung von Klassen, bei denen z.B. eine Tochterklasse eine in der Elternklasse definierte Operation mit geänderten Parametern aufrufen muss

Polymorphismen können mithilfe einer Vielzahl verschiedener Techniken erreicht werden. Gerade bei der Softwareentwicklung mittels .net ist wohl die Überladung eine der am häufigsten eingesetzten Technik.

//version 1 
public int GetSum(int val1, int val2)
{ 
    return val1 + val2; 
} 

//version 2 
public int GetSum(int[] values)
{ 
    int result = 0; 
    
    foreach(int val in values)
    { 
        result += val; 
    } 
    
    return result; 
} 

//version 3 
public string GetSum(string val1, string val2)
{ 
    int result = int.Parse(val1) + int.Parse(val2); 
    return result.ToString(); 
}

Wie in diesem Beispiel, besitzen sich überladende Methoden zwar den gleichen Namen, unterscheiden sich aber in den Argumenten und/ oder Rückgabetypen.

Das Überschreiben ist eine Methode, die gern im Zusammenhang mit Klassenvererbung eingesetzt wird. Dabei haben die Methoden in der Eltern- und auch der Kinderklasse den gleichen Namen, jedoch wird die Methode in der abgeleiteten Klasse aufgerufen – anstatt die in der Elternklasse. Voraussetzung dafür ist es (in C#), die Methode in der Elternklasse als virtual zu definieren und die Methode in der Kinderklasse mit override zu dekorieren.

//die Eltern-Klasse 
public class ParentClass
{ 
    public virtual void DoSomething()
    { 
        MessageBox.Show(DateTime.Now.ToString()); 
    } 
} 

//die Kind-Klasse 
public class ChildClass : ParentClass
{ 
    public override void DoSomething()
    { 
        MessageBox.Show("Hello World!"); 
    }
}

Eine besondere Form des Polymorphismus hat sich in JavaScript herausgebildet: Häufig müssen Eigenheiten der verschiedenen Browser bei der Erstellung von Methoden berücksichtigt werden. Häufig unterstützen einzelne Browser Variablen oder Methoden, die ein anderer gar nicht kennt und umgekehrt. Ein Beispiel dafür, ist document.documentMode in Microsofts Internet Explorer:

if (document.documentMode == 7) { 
     doSomethingIn.IE7_only; 
} else { 
     doSomething.everwhereElse; 
}

Eine Prüfung auf eine dieser Eigenschaften kann in der Praxis dafür verwendet werden um einen bestimmten Browser(-Typ) zur Laufzeit zu identifizieren und spezifische Operationen auszuführen.

Mithilfe sogenannter Polyfills ist es möglich in JavaScript Methoden für einen Browser zur Verfügung zu stellen, die dieser nativ gar nicht zur Verfügung stellt – ggf. auch um Code kompatibel zu älteren Browser-Versionen zu machen. So wird die string.startsWith()-Methode erst ab Version 12 nativ durch den Internet Explorer unterstützt, mithilfe eines Polyfills ist es aber möglich ältere Systeme damit „nachzurüsten“. Damit agieren Polyfills ähnlich den Extension-Methoden in z.B. VB.net oder C#.

//add the 'string.startsWith()' function (polyfill) (e.g. for the Internet Explorer) 
if (!String.prototype.startsWith) { 
    String.prototype.startsWith = function(searchString, position) { 
        position = position || 0; 
        return this.indexOf(searchString, position) === position; 
    }; 
}

Neben der Vererbung von Klassen selbst, gehören Polymorphismen zu den im Rahmen von Clean Code-Initiativen am stärksten diskutierten und kritisierten Themen. Als Hauptargumente gegen derartige Techniken wird häufig eine erhöhe Komplexität bzw. Unübersichtlichkeit des Quellcodes aufgeführt. Ich gebe diesen Kritikern recht, dass zumindest Überladungen und Überschreibungen zu Verwirrung und Code-Wiederholungen führen können, jedoch bin ich mir auch nicht ganz sicher wie wartbar ein Quellcode wird, in dem ich z.B. ständig vermeidbare Typen-Konversionen durchführen muss oder während der Laufzeit nicht in der Lage bin flexibel auf Abweichungen reagieren zu können.

Sofern notwendig, würde ich lieber für einige Tage durch das Fenster in das Kinderzimmer klettern, als die Wand zu durchbrechen um eine zweite Tür einzubauen, die u.U. die Statik des Gesamtgebäudes gefährdet.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.