Überblick Programmierparadigmen

Was ist ein Programmierparadigma und warum gibt es verschiedene?

Das Grundkonzept: Eine Denkweise für Code

Ein Programmierparadigma ist wie eine grundlegende Philosophie oder ein Denkansatz, nach dem Software entwickelt wird. Es gibt vor, wie du über Probleme nachdenkst und wie du die Struktur und Organisation deines Programmcodes gestaltest. Stell dir vor, du möchtest ein komplexes LEGO-Modell bauen. Es gibt verschiedene Herangehensweisen: Du könntest streng nach einer detaillierten Schritt-für-Schritt-Anleitung vorgehen (das wäre eher prozedural) oder du könntest das Modell in kleinere, wiederverwendbare Baustein-Module zerlegen, die jeweils eigene Funktionen haben und dann zusammengesetzt werden (das ginge in Richtung objektorientiert oder modular). Ein Programmierparadigma ist also nicht eine spezifische Programmiersprache, sondern ein übergeordnetes Konzept, das oft von vielen Sprachen unterstützt wird. Die Notwendigkeit verschiedener Paradigmen ergibt sich daraus, dass unterschiedliche Arten von Problemen und Softwareprojekten nach unterschiedlichen Denkweisen und Strukturierungsansätzen verlangen. Es gibt nicht das eine beste Paradigma; die Wahl hängt oft von der spezifischen Aufgabe, den Anforderungen an die Software und den Vorlieben des Entwicklungsteams ab.

Prozedurale Programmierung: Schritt für Schritt zum Ziel

Die prozedurale Programmierung ist eines der ältesten und grundlegendsten Paradigmen. Hierbei wird ein Programm in kleinere, wiederverwendbare Teile zerlegt, sogenannte Prozeduren oder Funktionen, die eine bestimmte Aufgabe erfüllen und nacheinander aufgerufen werden, um das Gesamtziel zu erreichen. Der Fokus liegt auf einer klaren Abfolge von Anweisungen. Stell dir ein detailliertes Kochrezept vor:

  1. Nimm 200g Mehl.
  2. Gib 2 Eier hinzu.
  3. Verrühre alles kräftig.

Das ist eine typische prozedurale Denkweise.

#include <stdio.h>

// Prozedur zur Berechnung der Summe
int berechneSumme(int a, int b) {
    return a + b;
}

// Hauptprozedur (Einstiegspunkt des Programms)
int main() {
    int zahl1 = 10;
    int zahl2 = 5;
    int ergebnis;

    ergebnis = berechneSumme(zahl1, zahl2); // Aufruf der Prozedur

    printf("Die Summe von %d und %d ist: %d\n", zahl1, zahl2, ergebnis);
    return 0;
}

Welche modernen Programmierparadigmen gibt es?

Objektorientierte Programmierung (OOP): Die Welt als Ansammlung von Objekten

Die objektorientierte Programmierung (OOP) strukturiert Software als eine Sammlung von interagierenden Objekten. Jedes Objekt ist eine Instanz einer Klasse (einer Art Bauplan) und bündelt Daten (Attribute), die seinen Zustand beschreiben, und Funktionen (Methoden), die auf diesen Daten operieren und sein Verhalten definieren. Ein reales Beispiel wäre ein Objekt Auto. Es könnte Attribute wie farbe = "rot", marke = "VW", aktuelleGeschwindigkeit = 0 haben und Methoden wie beschleunigen(wert), bremsen() oder hupen().

class Auto:
    # Konstruktor zum Erstellen eines Auto-Objekts
    def __init__(self, marke, farbe):
        self.marke = marke  
        self.farbe = farbe  
        self.geschwindigkeit = 0

    # Methode zum Beschleunigen
    def beschleunigen(self, wert):
        self.geschwindigkeit += wert
        print(f"Das {self.farbe}e {self.marke}-Auto beschleunigt auf {self.geschwindigkeit} km/h.")

    # Methode zum Bremsen
    def bremsen(self, wert):
        self.geschwindigkeit -= wert
        if self.geschwindigkeit < 0:
            self.geschwindigkeit = 0
        print(f"Das {self.farbe}e {self.marke}-Auto bremst auf {self.geschwindigkeit} km/h.")

# Erstellen von Objekten (Instanzen der Klasse Auto)
meinKaefer = Auto("VW", "Lila")
deinFlitzer = Auto("Porsche", "Gelb")

meinKaefer.beschleunigen(50)
deinFlitzer.beschleunigen(120)
meinKaefer.bremsen(20)

Funktionale Programmierung: Funktionen im Mittelpunkt

Die funktionale Programmierung behandelt Berechnungen als die Auswertung mathematischer Funktionen. Im Idealfall sind diese Funktionen "rein", das heißt, sie haben keine Seiteneffekte (sie verändern keinen Zustand außerhalb ihrer selbst, z.B. globale Variablen) und liefern für dieselbe Eingabe immer dieselbe Ausgabe. Datenstrukturen werden oft als unveränderlich (immutable) behandelt – einmal erstellt, werden sie nicht mehr modifiziert, stattdessen werden neue Datenstrukturen als Ergebnis von Operationen erzeugt. Anstatt eine Liste von Zahlen mit einer Schleife zu durchlaufen, um jede Zahl zu verdoppeln, würde man eine Funktion definieren, die eine Zahl verdoppelt, und diese Funktion auf jedes Element der Liste anwenden (z.B. mit einer map-Funktion).

const zahlen = [1, 2, 3, 4, 5];

// Eine reine Funktion, die eine Zahl verdoppelt
function verdoppeln(zahl) {
    return zahl * 2;
}

// Die map-Funktion wendet 'verdoppeln' auf jedes Element von 'zahlen' an
// und erzeugt ein neues Array, ohne das Original zu verändern.
const verdoppelteZahlen = zahlen.map(verdoppeln);

// Ausgabe: [2, 4, 6, 8, 10]
console.log(verdoppelteZahlen); 
// Ausgabe: [1, 2, 3, 4, 5] (Original bleibt unverändert)
console.log(zahlen);          

Logische Programmierung: Regeln und Fakten definieren die Lösung

Die logische Programmierung basiert auf der formalen Logik. Ein Programm besteht hierbei aus einer Menge von logischen Aussagen, die als Fakten (Grundwahrheiten) und Regeln (Schlussfolgerungen) definiert werden. Die Ausführung eines Programms ist ein Prozess des logischen Schließens (Inferenz), bei dem das System versucht, eine gestellte Anfrage (Query) basierend auf den gegebenen Fakten und Regeln zu beantworten. Man beschreibt also eher, was das Ergebnis sein soll, und nicht detailliert, wie es Schritt für Schritt berechnet wird.

% Fakten: definieren Beziehungen
elternteil(hans, ute).      % Hans ist Elternteil von Ute.
elternteil(maria, ute).     % Maria ist Elternteil von Ute.
elternteil(klaus, hans).    % Klaus ist Elternteil von Hans.
maennlich(hans).
maennlich(klaus).
weiblich(maria).
weiblich(ute).

% Regeln: definieren komplexere Beziehungen basierend auf Fakten
vater(Vater, Kind) :-           % Vater ist Vater von Kind, WENN
    elternteil(Vater, Kind),    % Vater Elternteil von Kind IST UND
    maennlich(Vater).           % Vater männlich IST.

grossvater(GV, Enkelkind) :-                    % GV ist Grossvater von Enkelkind, WENN
    vater(GV, ElternteilDesEnkels),             % GV Vater von ElternteilDesEnkels IST UND
    elternteil(ElternteilDesEnkels, Enkelkind). % ElternteilDesEnkels Elternteil von Enkelkind IST.

% Mögliche Anfragen (Queries) an das System:
% ?- vater(hans, ute).        % Ist Hans Vater von Ute? -> Antwort: true.
% ?- grossvater(klaus, ute).  % Ist Klaus Großvater von Ute? -> Antwort: true.
% ?- grossvater(X, ute).      % Wer (X) ist Großvater von Ute? -> Antwort: X = klaus.

Lernziele

  • die grundlegenden Unterschiede zwischen den wichtigsten Programmierparadigmen vergleichen, indem die Merkmale und Prinzipien von prozeduraler, objektorientierter, funktionaler und logischer Programmierung anhand konkreter Beispiele gegenübergestellt werden.
  • das Konzept eines Programmierparadigmas erklären, indem die grundlegende Idee, der Zweck und die Notwendigkeit verschiedener Ansätze zur Softwareentwicklung dargelegt werden.

Genug gelesen? Zeit, es wirklich zu können!

Die Theorie aus diesem Artikel ist die perfekte Basis. In der asyoube Lernplattform wendest du dieses Wissen an, bekommst persönliches Feedback und bereitest dich interaktiv auf deine Ausbildung oder deine Prüfungen vor.

Für Ausbilder & Unternehmen

Möchten Sie Ihr gesamtes Team mit asyoube ausbilden? Entdecken Sie unsere B2B-Lösung mit einfacher Verwaltung, Fortschritts-Tracking für Ihre Azubis und attraktiven Team-Preisen.