Menu
Coddy logo textTech

Singleton-Muster

Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 54 von 61.

Manchmal benötigen Sie genau eine Instanz eines Objekts in Ihrem gesamten Programm – 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-Variable innerhalb einer Quelldatei. Denken Sie daran, dass static auf Dateiebene bedeutet, dass die Variable privat für diese Datei ist und über die gesamte Lebensdauer des Programms hinweg bestehen bleibt. In Kombination mit einer Getter-Funktion erzeugt dies 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 passiert in get_config(). Der erste Aufruf erstellt und initialisiert die Instanz. Jeder nachfolgende Aufruf gibt denselben Pointer zurück.

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

Vom Header aus sehen Benutzer nur die Getter-Funktion – sie können nicht direkt auf die statische Variable zugreifen oder diese ändern. Egal wie oft oder von wo aus Sie get_config() aufrufen, Sie erhalten immer dasselbe Objekt.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Settings-Modul erstellen, das die anwendungsweite Konfiguration mithilfe des Singleton-Musters verwaltet. Egal wie oft Ihr Code die Einstellungen anfordert, er sollte immer genau dieselbe Instanz erhalten.

Sie werden Ihren Code auf drei Dateien verteilen:

  • settings.h: Deklarieren Sie einen opaken Settings-Typ mithilfe einer Vorwärtsdeklaration (der Rumpf des Structs bleibt verborgen). Deklarieren Sie eine Funktion get_settings, 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 unerlässlich.
  • settings.c: Hier passiert die Magie. Definieren Sie das eigentliche Settings-Struct mit den Feldern volume und brightness. Erstellen Sie eine static Zeigervariable, um die einzelne Instanz zu halten (initialisiert mit NULL). Implementieren Sie get_settings so, dass der erste Aufruf die Instanz mit Standardwerten (volume: 50, brightness: 75) alloziert und initialisiert, während nachfolgende Aufrufe einfach die vorhandene Instanz zurückgeben. Implementieren Sie die Getter und Setter, um auf die verborgenen Felder zuzugreifen und diese zu ändern.
  • main.c: Demonstrieren Sie, dass das Singleton korrekt funktioniert. Lesen Sie zwei Ganzzahlen ein, die neue Werte für Lautstärke und Helligkeit darstellen. Rufen Sie get_settings zweimal auf und speichern Sie die Ergebnisse in zwei verschiedenen Zeigervariablen. Verwenden Sie den ersten Zeiger, um die Lautstärke und Helligkeit mit den Eingabewerten zu aktualisieren. Verwenden Sie dann 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.

Ihr Programm erhält zwei Eingaben:

  1. Der neue Lautstärkewert
  2. Der neue Helligkeitswert

Geben Sie die Lautstärke und die Helligkeit in separaten Zeilen aus, nachdem Sie sie über einen Zeiger geändert und über einen anderen gelesen haben.

Beispielausgabe, wenn die Eingaben 80 und 100 sind:

80
100

Beispielausgabe, wenn die Eingaben 25 und 60 sind:

25
60

Beispielausgabe, wenn die Eingaben 0 und 50 sind:

0
50

Die entscheidende Erkenntnis ist, dass beide Zeiger auf dasselbe Objekt im Speicher zeigen – das ist das Wesen des Singleton-Musters. Die statische Variable in Ihrer Quelldatei stellt sicher, dass jemals nur eine Instanz existiert und diese über die gesamte Lebensdauer des Programms bestehen bleibt.

Spickzettel

Das Singleton-Muster stellt sicher, dass während des gesamten Programms nur eine Instanz eines Objekts existiert, und bietet einen globalen Zugriffspunkt darauf.

In C implementieren Sie ein Singleton mithilfe einer static-Variablen innerhalb einer Quelldatei in Kombination mit einer Getter-Funktion:

// 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;
}

Wichtige Punkte:

  • Das Schlüsselwort static auf Dateiebene macht die Variable für diese Datei privat und sorgt dafür, dass sie über die gesamte Lebensdauer des Programms bestehen bleibt
  • Die Getter-Funktion prüft beim ersten Aufruf, ob die Instanz NULL ist, erstellt und initialisiert sie und gibt sie dann zurück
  • Nachfolgende Aufrufe geben denselben Zeiger zurück
  • Benutzer sehen nur die Getter-Funktion über den Header — sie können nicht direkt auf die statische Variable zugreifen oder diese ändern
  • Egal wie oft oder von wo aus Sie den Getter aufrufen, Sie erhalten immer dasselbe Objekt

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: get_settings zweimal aufrufen und in zwei verschiedenen Zeigervariablen speichern
    
    
    // TODO: Den ersten Zeiger verwenden, um volume und brightness mit den Eingabewerten zu aktualisieren
    
    
    // TODO: Den zweiten Zeiger verwenden, 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 Object Oriented Programming