Menu
Coddy logo textTech

Singleton-Muster

Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 54 von 61.

Manchmal benötigst du in deinem gesamten Programm genau eine Instanz eines Objekts: einen Konfigurationsmanager, ein Protokollierungssystem oder eine Datenbankverbindung. Das Singleton-Muster stellt sicher, dass nur eine Instanz existiert, und bietet einen globalen Zugriffspunkt darauf.

In C implementieren wir dies mithilfe einer static-Variablen innerhalb einer Quelldatei. Denke daran, dass static auf Dateiebene bedeutet, dass die Variable für diese Datei privat ist und während der gesamten Lebensdauer des Programms bestehen bleibt. Zusammen mit einer Getter-Funktion entsteht dadurch unser Singleton:

// config.c
#include "config.h"
#include <stdlib.h>

struct Config {
    int max_connections;
    int timeout;
};

static Config* instance = NULL;

Config* get_config(void) {
    if (instance == NULL) {
        instance = malloc(sizeof(Config));
        instance->max_connections = 100;
        instance->timeout = 30;
    }
    return instance;
}

Die Magie geschieht in get_config(). Der erste Aufruf erstellt und initialisiert die Instanz. Jeder nachfolgende Aufruf gibt denselben Zeiger zurück.

Das Schlüsselwort static stellt sicher, dass instance seinen Wert zwischen Funktionsaufrufen beibehält und vor anderen Dateien verborgen bleibt.

Aus der Header-Datei sehen Benutzer nur die getter-Funktion. Sie können nicht direkt auf die statische Variable zugreifen oder sie ändern. Unabhängig davon, wie oft oder von wo aus Sie get_config() aufrufen, erhalten Sie immer dasselbe Objekt.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Settings-Modul, das die anwendungsweite Konfiguration mithilfe des Singleton-Musters verwaltet. Unabhängig davon, wie oft dein Code die Einstellungen anfordert, sollte er immer genau dieselbe Instanz erhalten.

Du organisierst deinen Code über drei Dateien:

  • settings.h: Deklariere einen opaken Typ Settings mithilfe einer Vorwärtsdeklaration (der Struct-Rumpf bleibt verborgen). Deklariere eine get_settings-Funktion, die einen Zeiger auf die Singleton-Instanz zurückgibt, sowie Getter- und Setter-Funktionen für zwei Konfigurationswerte: volume (eine Ganzzahl) und brightness (eine Ganzzahl). Include-Guards sind hier unbedingt erforderlich.
  • settings.c: Hier geschieht die Magie. Definiere die tatsächliche Settings-Struktur mit den Feldern volume und brightness. Erstelle eine static Zeigervariable, die die einzige Instanz speichert (initialisiert mit NULL). Implementiere get_settings so, dass der erste Aufruf die Instanz allokiert und mit Standardwerten (volume: 50, brightness: 75) initialisiert, während nachfolgende Aufrufe einfach die vorhandene Instanz zurückgeben. Implementiere die Getter und Setter, um auf die verborgenen Felder zuzugreifen und sie zu ändern.
  • main.c: Zeige, dass das Singleton korrekt funktioniert. Lies zwei Ganzzahlen ein, die neue volume- und brightness-Werte darstellen. Rufe get_settings zweimal auf und speichere die Ergebnisse in zwei verschiedenen Zeigervariablen. Verwende den ersten Zeiger, um volume und brightness mit den Eingabewerten zu aktualisieren. Verwende anschließend den zweiten Zeiger, um beide Werte abzurufen und auszugeben. Wenn das Singleton funktioniert, sind Änderungen, die über einen Zeiger vorgenommen wurden, über den anderen sichtbar.

Dein Programm erhält zwei Eingaben:

  1. Den neuen volume-Wert
  2. Den neuen brightness-Wert

Gib volume und brightness nach ihrer Änderung über einen Zeiger und dem Auslesen über einen anderen in separaten Zeilen aus.

Beispielausgabe bei den Eingaben 80 und 100:

80
100

Beispielausgabe bei den Eingaben 25 und 60:

25
60

Beispielausgabe bei den Eingaben 0 und 50:

0
50

Die entscheidende Erkenntnis ist, dass beide Zeiger auf dasselbe Objekt im Speicher zeigen: Das ist das Wesentliche des Singleton-Musters. Die statische Variable innerhalb deiner Quelldatei stellt sicher, dass immer nur eine Instanz existiert, und sie bleibt während der gesamten Laufzeit des Programms erhalten.

Probier es selbst

#include <stdio.h>
#include "settings.h"

int main() {
    int new_volume, new_brightness;
    scanf("%d", &new_volume);
    scanf("%d", &new_brightness);
    
    // TODO: Rufe get_settings zweimal auf und speichere in zwei verschiedenen Zeigervariablen
    
    
    // TODO: Verwende den ersten Zeiger, um volume und brightness mit den Eingabewerten zu aktualisieren
    
    
    // TODO: Verwende den zweiten Zeiger, um volume und brightness abzurufen und auszugeben
    // (Dies zeigt, dass beide Zeiger auf dieselbe Singleton-Instanz zeigen)
    
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C-Compiler