Programmierparadigma
Ein Programmierparadigma ist ein fundamentaler Programmierstil.[1] „Der Programmierung liegen je nach Design der einzelnen Programmiersprache verschiedene Prinzipien zugrunde. Diese sollen den Entwickler bei der Erstellung von ‚gutem Code‘ unterstützen, in manchen Fällen sogar zu einer bestimmten Herangehensweise bei der Lösung von Problemen zwingen“.[2]
Programmierparadigmen unterscheiden sich durch ihre Konzepte für die Repräsentation von statischen (wie beispielsweise Objekte, Methoden, Variablen, Konstanten) und dynamischen (wie beispielsweise Zuweisungen, Kontrollfluss, Datenfluss) Programmelementen.
Grundlegend für den Entwurf von Programmiersprachen sind die Paradigmen der imperativen und der deklarativen Programmierung. Beim letzteren sind als wichtige Ausprägungen die Paradigmen der funktionalen Programmierung und der logischen Programmierung zu nennen.
Die verschiedenen Paradigmen sind, bezogen auf einzelne Computerprogramme, nicht als komplementäre bzw. alternative Programmierstile zu verstehen. Vielmehr können „viele Programmiersprachen mehrere Paradigmen gleichzeitig unterstützen“.[3]
- Beispiel
- In einer unter MS Access und mit VBA entwickelten Anwendung sind die funktionalen Komponenten ereignis- und objektorientiert angelegt (Bsp.: „beim Öffnen von Formular X“). Der VBA-Code ist strukturiert/modular/prozedural (denn er besteht aus Modulen, Makros, Prozeduren etc.); und er ist gleichzeitig imperativ, weil er „Befehle“ enthält, die (innerhalb der Prozeduren…) exakt in der codierten Folge ausgeführt werden. Formulare und Berichte sowie die SQL-Aufrufe sind deklarativ, weil der Entwickler hier nur das WAS und nicht das WIE festlegt.
Trotzdem werden Programmiersprachen häufig – nach ihrer Grund-Charakteristik – z. B. als objektorientierte, prozedurale oder deklarative Sprache bezeichnet. Die ein Paradigma bestimmenden Merkmale beruhen überwiegend auf den Eigenschaften der angewendeten Programmiersprachen (wie z. B. bei OOP, deklarativ …), zum Teil aber auch auf dem individuellen Stil, der beim Programmieren praktiziert wird (z. B. strukturiert, modular).
Oft kann an den „Bürgern erster Klasse“ („First Class Citizens“ – FCCs) einer Programmiersprache – also den Formen von Daten, die direkt verwendet werden können – erkannt werden, welchem Paradigma die Sprache gehorcht. In Java (objektorientiert) zum Beispiel sind Objekte FCCs, in Lisp ist jedes Stück Programm FCC, in Perl sind es Zeichenketten, Arrays und Hashes.
Neben den Programmierparadigmen gibt es noch eine Reihe weiterer Kriterien für die Entwicklung einer möglichst fehlerfreien und wartbaren Software – wie zum Beispiel Lesbarkeit des Programmcodes, Redundanzfreiheit, Modularität und Nebenwirkungsfreiheit. Diese sollten unter jedem Paradigma so weit wie möglich eingehalten werden. Siehe auch Programmierstil.
Imperative Programmierung
Bei der imperativen Programmierung „besteht ein Programm (Anm.: d. h. hier der Quellcode) aus einer Folge von Befehlen, die vorgeben, in welcher Reihenfolge was vom Computer getan werden soll“.[2] Die Bezeichnung basiert auf lateinisch „imperare“ („anordnen“, „befehlen“).
Strukturierte Programmierung
Eine Weiterentwicklung imperativer Sprachen markierte der Aufsatz Go To Statement Considered Harmful von Edsger W. Dijkstra aus dem Jahr 1968. Darin wird der Verzicht oder zumindest die Einschränkung der absoluten Sprunganweisungen (Goto) gefordert; stattdessen sollen Kontrollstrukturen, wie zum Beispiel „if… then…“, „case… of…“, „while… do…“, „repeat… until…“ verwendet werden.
Prozedurale Programmierung
Den Ansatz, Programme in kleinere Teilaufgaben aufzuspalten, bezeichnet man als prozedurale Programmierung. Die entstehenden Teilprogramme werden Prozeduren genannt. Praktisch alle aktuellen imperativen Programmiersprachen beinhalten den prozeduralen Ansatz.
Die Entwicklung prozeduraler Programmiersprachen und -techniken waren ein wesentlicher Schritt zwischen Assemblersprache und Hochsprachen, indem sie Abstraktion und Zerlegung von Algorithmen ermöglichen.
Modulare Programmierung
Modulare Programmierung war der erste Versuch, der wachsenden Größe von Softwareprojekten Herr zu werden. In der modularen Programmierung wird der prozedurale Ansatz erweitert, indem Prozeduren zusammen mit Daten in logischen Einheiten zusammengefasst werden. Die Software wird so in größere funktionale Teilblöcke zerlegt, die einzeln geplant, programmiert und getestet werden können. Die entstehenden Unterprogramme werden als Module bezeichnet. Am Ende können die Einzelteile dann logisch miteinander verknüpft werden und die Software ist einsatzbereit. Die normierte Programmierung beschreibt dabei den Versuch, diesen Ablauf zu standardisieren. Die erste streng modularisierte Programmiersprache war 1978 Modula-2.
Programmierung mit abstrakten Datentypen
Die Programmierung mit abstrakten Datentypen behandelt Daten und Funktionen zur Behandlung dieser Daten als Einheit.
Deklarative Programmierparadigmen
Die Idee einer deklarativen Programmierung ist der historisch jüngere Ansatz. Im Gegensatz zu imperativen Programmierparadigmen, bei denen das Wie im Vordergrund steht, fragt man in der deklarativen Programmierung nach dem Was, das berechnet werden soll. Es wird also nicht mehr der Lösungsweg programmiert, sondern nur noch angegeben, welches Ergebnis gewünscht ist. Zu diesem Zweck beruhen deklarative Paradigmen auf mathematischen, rechnerunabhängigen Theorien.
- Aufgrund der referenziellen Transparenz gibt es keine Nebeneffekte. Programme sind damit teilweise auswertbar und ermöglichen so zum Beispiel die Behandlung unendlicher Datenstrukturen.
- Beweise (zum Beispiel Korrektheitsbeweis, Beweise über Programmeigenschaften) sind dank mathematischer Basis (unter anderem Lambda-Kalkül) uneingeschränkt durchführbar.
- Architekturunabhängigkeit
- teilweise geringe Akzeptanz (man spricht gern von sogenannten Akademikersprachen)
Zu den deklarativen Programmiersprachen gehören:
- funktionale Sprachen (unter anderem Lisp, ML, Miranda, Gofer, Haskell, F#, Scala)
- logische Sprachen (unter anderem Prolog)
- funktional-logische Sprachen (unter anderem Babel, Escher, Curry, Oz)
- mengen-orientierte Abfragesprachen (unter anderem SQL)
Beispiel Quicksort: Haskell ist eine typische deklarative Programmiersprache. Der Programmierer beschreibt, was das Programm mit einer Eingabe macht, also wie mit welcher Eingabe umzugehen ist, wobei der Berechnungsablauf nicht von Interesse ist. Die Berechnungen erfolgen dann durch Wertemanipulation. Hauptkontrollstruktur bildet die Rekursion, insbesondere aus Effizienzgründen die Endrekursion:
quicksort [] = []
quicksort (x:xs) = quicksort [n | n<-xs, n<x] ++ [x] ++ quicksort [n | n<-xs, n>=x]
Funktionale Programmierung
Die Aufgabenstellung und die bekannten Prämissen werden hier als funktionaler Ausdruck formuliert. Das selbstständige Anwenden von Funktionsersetzung und Auswertung seitens des Interpreters oder Compilers lösen dann die Aufgabenstellung. Das Programm kann als Abbildung der Eingabe auf die Ausgabe aufgefasst werden.
Logische Programmierung
Die Aufgabenstellung und ihre Prämissen werden als logische Aussagen (Regeln) formuliert (vgl. funktionale Programmierung, s. o.). Der Interpreter versucht dann, die gewünschte Lösungsaussage herzuleiten. Bei anderen regelbasierten Sprachen wie OPS-5, XSLT oder Prolog werden Regeln gegen eine Datenmenge auf ihre Instanziierbarkeit geprüft. Aus allen Regelinstanziierungen wird eine (mehrere, alle) ausgewählt und die zur Regel gehörenden Anweisungen werden ausgeführt.
Constraintprogrammierung
Bei der Constraintprogrammierung werden Constraints definiert. Sie wird als natürliche Weiterentwicklung der logischen Programmierung verstanden. Logische und Constraintprogrammierung werden typischerweise in Kombination eingesetzt.
Objektorientierte Programmierparadigmen
Klassen sind instanziierbare Module und Grundelemente in der objektorientierten Programmierung. Nach dem objektorientierten Programmierparadigma werden Objekte mit Daten und den darauf arbeitenden Routinen zu Einheiten zusammengefasst. Im Unterschied dazu werden beim prozeduralen Paradigma die Daten von den die Objekte verarbeitenden Routinen getrennt gehalten. Ein Computerprogramm ist realisiert als eine Menge interagierender Objekte.
Objektorientierte Programmierung lässt sich gut mit der ereignisorientierten Programmierung kombinieren, z. B. bei der Programmierung interaktiver, grafischer Benutzeroberflächen.
Komponentenorientierte Programmierparadigmen
Mithilfe der komponentenbasierten Entwicklung von Software mit abstrakten und generischen Komponentenmodellen können Softwarekomponenten mit streng definierten Schnittstellen erstellt werden.
Durch die Wiederverwendbarkeit von Software-Bausteinen kann der Entwicklungsaufwand und die Fehleranfälligkeit reduziert sowie die Informationssicherheit erhöht werden. Durch die vollständige und strenge Datenkapselung wird ferner die Portabilität von Daten und Programmen erleichtert.
Agentenorientierte Programmierung
Bei der agentenorientierten Programmierung steht der Begriff des autonomen und planenden Agenten im Vordergrund, der selbstständig und in Kooperation mit anderen Agenten Probleme löst.
Aspektorientierte Programmierung
Bei der aspektorientierten Programmierung wird der objektorientierte Begriff der Klasse zum Aspekt erweitert und ermöglicht so orthogonale Programmierung.
Generative Programmierung
Generative Programmierung ist ein Überbegriff für die programmatische Erzeugung von Sourcecode (Siehe Buch: Generative Programming, Krzysztof Czarnecki, W. Eisenecker)
Generische Programmierung
In der generischen Programmierung wird versucht, die Algorithmen für mehrere Datentypen verwendbar zu gestalten.
Subjektorientierte Programmierung
Die Subjektorientierte Programmierung ist eine Erweiterung der objektorientierten Programmierung. Sie soll vor allem die Schwächen der objektorientierten Programmierung bei der Entwicklung großer Anwendungen und der Integration unabhängig entwickelter Anwendungen ausgleichen.
Datenstromorientierte Programmierung
Es wird von einem kontinuierlichen Datenfluss ausgegangen (meist Audio- oder Videodaten), der (oft in Echtzeit) verändert und ausgegeben wird.
Graphersetzung
Bei der Graphersetzung werden die Daten in Form von Graphen modelliert und die Berechnungen durch Graphersetzungsregeln spezifiziert, durch deren gesteuerte Anwendung ein gegebener Arbeitsgraph Stück für Stück umgeformt wird.
Konkatenative Programmierung
Konkatenative Programmierung zeichnet sich dadurch aus, dass Programme durch die einfache Verkettung von (Programmier-)Wörtern gebildet werden. Diese operieren alle auf derselben Datenstruktur, welche weitergereicht wird, ohne dass dazu eine weitergehende komplexe Syntax nötig wäre. Das führt dazu, dass eine natürlichsprachliche Syntax wie A B C möglich ist, für die in anderen Sprachen beispielsweise C(B(A(x))) geschrieben werden müsste. Konkatenative Programmiersprachen sind daher meistens aus Implementierungsgründen auch stapelbasierte Sprachen, wie zum Beispiel Forth, Joy, Cat und PostScript. Im Gegensatz dazu sind applikative Programmiersprachen solche, die Funktionen auf Argumente anwenden. In diese Kategorie fallen die meisten imperativen und funktionalen Sprachen.[4][5]
Multiparadigmatische Programmierung
Multiparadigmatische Sprachen vereinigen verschiedenen Paradigmen miteinander. Beispiele sind F-Sharp, Lisp, OCaml, Swift, und Wolfram Language.
Weblinks
- Martin Grabmüller: Multiparadigmen-Programmiersprachen. (PDF; 461 kB) Technische Universität Berlin, abgerufen am 3. April 2013.
Einzelnachweise
- Bjarne Stroustrup: Conference on History of Programming Languages: Evolving a language in and for the real world: C++ 1991-2006. (PDF) Abgerufen am 20. September 2010 (englisch): „C++ supports several programming styles or, as they are sometimes somewhat pretentiously called, “programming paradigms”.“
- Andreas M. Böhm, Bettina Jungkunz: Grundkurs IT-Berufe. Vieweg, 2005, ISBN 978-3-528-05913-2.
- Funktionale Programmierung oder imperative Programmierung? In: MSDN. Microsoft, abgerufen am 3. April 2013.
- Concatenative language. Concatenative, abgerufen am 20. September 2010 (englisch).
- Dominikus Herzberg, Tim Reichert: Concatenative Programming. (PDF; 315 kB) 2009, archiviert vom Original am 27. Februar 2012; abgerufen am 20. September 2010 (englisch).