Wzorzec Singleton
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 54 z 61.
Czasami w całym programie potrzebujesz dokładnie jednej instancji obiektu: menedżera konfiguracji, systemu logowania lub połączenia z bazą danych. Wzorzec Singleton gwarantuje, że istnieje tylko jedna instancja, i zapewnia globalny punkt dostępu do niej.
W języku C implementujemy to za pomocą zmiennej static wewnątrz pliku źródłowego. Pamiętaj, że static na poziomie pliku oznacza, że zmienna jest prywatna dla tego pliku i istnieje przez cały czas działania programu. W połączeniu z funkcją pobierającą tworzy to nasz 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;
}Magia dzieje się w get_config(). Pierwsze wywołanie tworzy i inicjalizuje instancję. Każde kolejne wywołanie zwraca ten sam wskaźnik.
Słowo kluczowe static sprawia, że instance zachowuje swoją wartość między wywołaniami funkcji i pozostaje ukryte przed innymi plikami.
Z nagłówka użytkownicy widzą tylko funkcję pobierającą. Nie mogą bezpośrednio uzyskiwać dostępu do zmiennej statycznej ani jej modyfikować. Bez względu na to, ile razy i z jakiego miejsca wywołasz get_config(), zawsze otrzymasz ten sam obiekt.
Wyzwanie
ŁatwyZbudujmy moduł Settings, który zarządza konfiguracją całej aplikacji za pomocą wzorca Singleton. Niezależnie od tego, ile razy kod poprosi o ustawienia, zawsze powinien otrzymać dokładnie tę samą instancję.
Rozmieścisz kod w trzech plikach:
settings.h: Zadeklaruj nieprzezroczysty typSettingsza pomocą deklaracji wstępnej (definicja struktury pozostaje ukryta). Zadeklaruj funkcjęget_settings, która zwraca wskaźnik do instancji Singletona, a także funkcje pobierające i ustawiające dwie wartości konfiguracyjne:volume(liczba całkowita) ibrightness(liczba całkowita). W tym przypadku strażniki dołączania są niezbędne.settings.c: Tutaj dzieje się cała magia. Zdefiniuj właściwą strukturęSettingsz polamivolumeibrightness. Utwórz zmienną wskaźnikowąstatic, która będzie przechowywać pojedynczą instancję (zainicjalizowaną jakoNULL). Zaimplementujget_settingstak, aby pierwsze wywołanie alokowało i inicjalizowało instancję wartościami domyślnymi (volume: 50, brightness: 75), a kolejne wywołania po prostu zwracały istniejącą instancję. Zaimplementuj funkcje pobierające i ustawiające, aby umożliwić dostęp do ukrytych pól i ich modyfikację.main.c: Zademonstruj, że Singleton działa poprawnie. Wczytaj dwie liczby całkowite reprezentujące nowe wartości volume i brightness. Wywołaj dwukrotnieget_settings, zapisując wyniki w dwóch różnych zmiennych wskaźnikowych. Użyj pierwszego wskaźnika, aby zaktualizować volume i brightness wartościami wejściowymi. Następnie użyj drugiego wskaźnika, aby pobrać i wypisać obie wartości. Jeśli Singleton działa poprawnie, zmiany wprowadzone za pomocą jednego wskaźnika będą widoczne za pomocą drugiego.
Program otrzyma dwa wejścia:
- Nową wartość volume
- Nową wartość brightness
Po ich zmodyfikowaniu za pomocą jednego wskaźnika i odczytaniu za pomocą drugiego wypisz volume i brightness w osobnych wierszach.
Przykładowe wyjście dla wejść 80 i 100:
80
100Przykładowe wyjście dla wejść 25 i 60:
25
60Przykładowe wyjście dla wejść 0 i 50:
0
50Najważniejsze jest to, że oba wskaźniki wskazują ten sam obiekt w pamięci — na tym polega istota wzorca Singleton. Zmienna statyczna w pliku źródłowym gwarantuje, że istnieje tylko jedna instancja, która pozostaje dostępna przez cały czas działania programu.
Spróbuj swoich sił
#include <stdio.h>
#include "settings.h"
int main() {
int new_volume, new_brightness;
scanf("%d", &new_volume);
scanf("%d", &new_brightness);
// TODO: Wywołaj get_settings dwukrotnie i zapisz wyniki w dwóch różnych zmiennych wskaźnikowych
// TODO: Użyj pierwszego wskaźnika, aby zaktualizować głośność i jasność wartościami wejściowymi
// TODO: Użyj drugiego wskaźnika, aby odczytać i wyświetlić głośność oraz jasność
// (To pokazuje, że oba wskaźniki wskazują na tę samą instancję singletonu)
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania modularnego
Pliki nagłówkoweZabezpieczenia przed wielokrotnym dołączeniemPliki źródłoweFunkcje statycznePodsumowanie: kalkulator modularny4Enkapsulacja
Koncepcja niejawnych wskaźnikówDefiniowanie niejawnych strukturGettery i setteryWalidacja w setterachPowtórka: tajemnicze pudełko2Obiekty i metody
Struktury jako obiektyWskaźnik „self”Poprawność constWskaźnik czy wartośćMetody pomocniczePodsumowanie: menedżer punktów5Projekt: Proste konto bankowe
Konfiguracja projektuImplementacja konta8Polimorfizm
Wskaźniki do funkcji w strukturachSymulowanie metodKoncepcja interfejsuImplementowanie interfejsówIteracja polimorficznaPodsumowanie: Greeter11Wzorce projektowe w C
Wzorzec SingletonWzorzec fabrykiWzorzec iteratoraPodsumowanie: fabryka loggera3Cykl życia obiektu
Wzorzec konstruktoraWzorzec destruktoraInicjalizacja na stosieKopia głębokaPodsumowanie: klasa opakowująca ciąg znaków6Dziedziczenie przez kompozycję
Osadzanie strukturZasada pierwszego elementuDostęp do elementów klasy nadrzędnejKonwersja w górę hierarchiiPodsumowanie: hierarchia kształtów9Projekt: Rysownik kształtów
Przegląd projektuImplementacja kołaImplementacja prostokątaZastosowanie polimorfizmuKontener kształtówPoćwicz samodzielnie: Kompilator C online