Generische Programmierung ist ein Verfahren zur Entwicklung wiederverwendbarerSoftware-Bibliotheken. Dabei werdenFunktionen möglichst allgemein entworfen, um sie für unterschiedlicheDatentypen undDatenstrukturen verwenden zu können.
Die Implementierung erfolgt bei einigenProgrammiersprachen durch das Konzeptgenerischer Typen bzw.Templates – so gestalten sichdynamische Programmiersprachen, bei denen sich der Typ einer Variable zur Laufzeit ändern darf, durch ihre verallgemeinertePolymorphie generisch. Von Sprachen, die solche Mechanismen bieten, sagt man auch, dass sieGenerik erlauben.
Wesentlich bei der generischen Programmierung ist, dass die Algorithmen nicht für einen bestimmtenDatentyp geschrieben werden, sondern nur bestimmte Anforderungen an die Typen stellen. Das Prinzip wird auchparametrische Polymorphie genannt.
Paradebeispiel ist dieC++-Standardbibliothek der ProgrammierspracheC++, bei der dieAlgorithmen so weit wie möglich von den Datenstrukturen, mit denen sie arbeiten, getrennt werden.
Ein Beispiel liefert die Maximum-Funktion
die für zwei gegebene Werte, desselben Typs den größeren Wert zurückgibt.
Voraussetzung ist, dass und miteinander vergleichbar sind, der Ausdruck also definiert ist und eineOrdnung beschreibt.
Derobjektorientierte Ansatz funktioniert im Kontext statischer Typisierung nicht zufriedenstellend. Zwar lassen sich hier mitInterfaces oder MehrfachableitungKlassen um „andere“ Datentypen erweitern, sodass sich mittels Polymorphie das Verhalten der generischen Programmierung zum Teil nachbauen lässt, allerdings wird die vollständige Unabhängigkeit von unterschiedlichen Typen (bzw. Klassen) damit nicht umgesetzt, weil es sich bei diesen Techniken auch nicht strenggenommen um objektorientierte Techniken handelt.
Definiert man in derProgrammierspracheC++ eineKlasseVergleichbares und leitet davon zum Beispiel für eine physikalischeSimulation die KlassenLaenge undMasse ab (sagt also, dass sowohl Längen als auch die Masse etwas vergleichbares sind), so kann man zwar schreiben:
Vergleichbares&max(Vergleichbares&a,Vergleichbares&b){if(a<b){returnb;}else{returna;}}
aber es gibt immer noch zwei Probleme:
Generische Programmierung, beispielsweise inC++ überTemplates realisiert, verbindet nun die Flexibilität des Makros mit derTypsicherheit und den anderen Eigenschaften der Funktion. Die generische Implementierung von max in C++ ist
template<typenameT>Tmax(Ta,Tb){if(a<b){returnb;}else{returna;}}
Die so definierte Funktionmax() kann nun für alle Typen mitVergleichsoperator verwendet werden.
Ein weiterer Vorteil ist, dass man nicht explizit bei der Definition sagen muss, dass ein Typ vergleichbar ist (zum Beispiel durch Ableiten von einer entsprechenden Klasse), sondern man muss nur sicherstellen, dass er die nötigen Eigenschaften hat (in diesem Fall einen operator < mit korrekterSemantik). Auf diese Weise können auch Typen mit der Funktion verwendet werden, die in Unkenntnis der Funktion entworfen wurden (beispielsweise die eingebauten Typen der Programmiersprache).
Ein Algorithmus sollte dabei stets möglichst wenig vom Typ fordern. So arbeiten die Algorithmen derSTL nicht direkt auf den Containern, sondern mitIteratoren. Auf diese Weise werden sie weitgehend unabhängig von den genauen Eigenschaften des speziellen Containers und können teilweise sogar direkt auf Ein- und Ausgabeströmen arbeiten.
Generische Programmierung in derProgrammierspracheC# ähnelt den Templates inC++. Beispielsweise verwenden Klassen wieHashSet,List,Dictionary usw.generische Typen sehr gut. In C# werden spitze Klammern verwendet, um die Parametertypen in der generischen Klassendeklaration anzugeben. Im folgenden Beispiel wird einObjekt einer generischenKlasse mit zwei Typparametern erstellt.
usingSystem;// Deklariert eine Klasse mit zwei Typparametern T und UclassTest<T,U>{Tobject1;// Deklariert ein Objekt vom Typ TUobject2;// Deklariert ein Objekt vom Typ U// Konstruktor der Klasse, der die zwei Objekte initialisiertpublicTest(Tobject1,Uobject2){this.object1=object1;this.object2=object2;}// Methode, die die zwei Objekte auf der Konsole ausgibtpublicvoidWrite(){Console.WriteLine(object1);Console.WriteLine(object2);}}// Hauptklasse, die die Hauptmethode enthältclassTestClass{// Hauptmethode die das Programm ausführtpublicstaticvoidMain(string[]args){Test<string,DateTime>testObject=newTest<string,DateTime>("Willkommen bei Wikipedia!",DateTime.Now);// Aufruf des KonstruktorstestObject.Write();// Aufruf der Methode, Ausgabe auf der KonsoleConsole.ReadLine();}}
EineMethode, die mit den Typparametern für ihren Rückgabetyp oder ihren Rückgabeparameter deklariert wird, wird alsgenerische Methode bezeichnet. Im folgenden Beispiel wird einObjekt einer generischenKlasse mit einer generische Methoden erstellt.
usingSystem;usingSystem.Collections.Generic;// Deklariert eine Klasse mit einem Typparameter TclassTest<T>{privateList<T>list=newList<T>();// Deklariert eine Liste mit dem Typparameter T// Diese Methode fügt am angegebenen Index ein Element in die Liste einpublicvoidInsert(intindex,Titem){list.Insert(index,item);}// Diese generische Methode gib das Element am angegebenen Index zurückpublicTGet(intindex){if(index>=0&&index<list.Count){returnlist[index];}returndefault(T);// Wenn der Index nicht in der Liste ist, wird der Standardwert vom Typ zurückgegeben}// Diese Methode gibt die Liste Zeile für Zeile zurückpublicoverridestringToString(){stringtext="";for(inti=0;i<list.Count;i++)// for-Schleife, die die Elemente durchläuft{text+=list[i]+"\r\n";}returntext;}}// Hauptklasse, die die Hauptmethode enthältclassTestClass{// Hauptmethode die das Programm ausführtpublicstaticvoidMain(string[]args){Test<string>testObject=newTest<string>();// Aufruf des Standard-Konstruktors mit dem Typparameter string// Fügt drei Elemente in die Liste eintestObject.Insert(0,"Werde Mitglied bei Wikipedia!");testObject.Insert(1,"Willkommen bei Wikipedia!");testObject.Insert(2,"Wie gefällt dir Wikipedia?");Console.WriteLine(testObject.ToString());// Aufruf der Methode, Ausgabe aller drei Elemente auf der KonsoleConsole.WriteLine(testObject.Get(1));// Aufruf der Methode, Ausgabe des zweiten Elements auf der KonsoleConsole.ReadLine();}}
Ein generischesModell ist an spezifische Gegebenheiten einer konkreten Situation anpassbar; z. B. ein generischesVorgehensmodell wieWasserfall- oderSpiralmodell an ein konkretesProjekt. Dieser Vorgang wird im Bereich desSoftware-Engineering auchTailoring genannt.
Ändert sich das generische Modell, kann man dessen Ausprägungen leicht anpassen, wenn der Weg vom Modell zur Ausprägung detailliert beschrieben ist und man nur die geänderten Elemente verfolgen muss. Bei nicht-generischen Modellen spricht man in diesem Zusammenhang auch vonÜberanpassung.