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.
Aufgabe
EinfachLassen 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 opakenSettings-Typ mithilfe einer Vorwärtsdeklaration (der Rumpf des Structs bleibt verborgen). Deklarieren Sie eine Funktionget_settings, die einen Zeiger auf die Singleton-Instanz zurückgibt, sowie Getter- und Setter-Funktionen für zwei Konfigurationswerte:volume(eine Ganzzahl) undbrightness(eine Ganzzahl). Include-Guards sind hier unerlässlich.settings.c: Hier passiert die Magie. Definieren Sie das eigentlicheSettings-Struct mit den Feldernvolumeundbrightness. Erstellen Sie einestaticZeigervariable, um die einzelne Instanz zu halten (initialisiert mitNULL). Implementieren Sieget_settingsso, 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 Sieget_settingszweimal 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:
- Der neue Lautstärkewert
- 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
100Beispielausgabe, wenn die Eingaben 25 und 60 sind:
25
60Beispielausgabe, wenn die Eingaben 0 und 50 sind:
0
50Die 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
staticauf 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
NULList, 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;
}
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
1Grundlagen der modularen Programmierung
Header-DateienInclude GuardsQuelldateienStatische FunktionenWiederholung: Modularer Taschenrechner4Kapselung
Konzept der Opaque PointersOpaque Structs definierenGetter und SetterValidierung in SetternRückblick: Die geheime Box2Objekte und Methoden
Structs als ObjekteDer 'Self'-PointerConst-CorrectnessPointer vs. WertHilfsmethodenZusammenfassung: Point Manager5Projekt: Einfaches Bankkonto
Projekt-SetupImplementierung des Kontos3Objekt-Lebenszyklus
Konstruktor-MusterDestruktor-MusterStack-InitialisierungTiefe KopieRückblick: String-Wrapper6Vererbung durch Komposition
Struct-EinbettungDie First-Member-RegelZugriff auf Parent-MemberUpcastingRückblick: Formenhierarchie9Projekt: Formen-Zeichner
ProjektübersichtKreis-Implementierung