January 19, 2026 (7mo ago) — last updated May 29, 2026 (3mo ago)

Singleton w TypeScript: kompletny przewodnik

Naucz się wzorca Singleton: zastosowania, implementacja w TypeScript, wpływ na testowalność i bezpieczne alternatywy jak DI i kontenery IoC.

← Back to blog
Cover Image for Singleton w TypeScript: kompletny przewodnik

Wzorzec Singleton zapewnia jedną instancję klasy i pojedynczy punkt dostępu do współdzielonego zasobu. Ten przewodnik pokaże, kiedy użyć Singletona, jak go bezpiecznie zaimplementować w TypeScript oraz jakie alternatywy — np. wstrzykiwanie zależności i kontenery IoC — warto rozważyć.

Opanowanie wzorca Singleton w TypeScript: Kompletny przewodnik

Szkic ołówkiem królewskiego pisarza w koronie, pilnie badającego świecący zwój na stole.

W świecie tworzenia oprogramowania niektóre narzędzia są potężne, ale wymagają ostrożnego stosowania. Wzorzec Singleton zapewnia, że dana klasa ma tylko jedną instancję i udostępnia pojedynczy, globalny punkt dostępu do niej. Zrozumienie, kiedy go użyć, jak go bezpiecznie zaimplementować w TypeScript i jakie istnieją alternatywy, pomoże ci podejmować lepsze decyzje projektowe1.

Czym jest wzorzec Singleton i kiedy jest użyteczny?

Wyobraź sobie średniowieczne królestwo z tylko jednym oficjalnym Królewskim Pisarzem. Ta osoba jest jedyną upoważnioną do zapisywania dekretów, dzięki czemu rejestry są spójne. Wzorzec Singleton działa podobnie w oprogramowaniu: ogranicza klasę tak, by mogła istnieć tylko jedna instancja, która staje się źródłem prawdy dla określonego zadania.

Główne zalety to centralizacja kontroli nad zasobami, ograniczenie duplikacji i oszczędność pamięci. Przykłady zastosowań obejmują menedżery konfiguracji, loggery czy puli połączeń do bazy danych. Jednak decyzja o użyciu Singletona wymaga rozważenia wpływu na testowalność i utrzymywalność kodu.

Główne przeznaczenie i analogia

Singleton to nie tylko sposób na blokadę tworzenia obiektów; to centralizacja odpowiedzialności. Tak jak Królewski Pisarz zapewnia jeden punkt dostępu do oficjalnych rejestrów, instancja Singleton oferuje uniwersalny dostęp do współdzielonego zasobu.

Klasycznym przykładem jest pula połączeń do bazy danych — nie chcesz, by każdy komponent otwierał osobne połączenie. Zamiast tego jedna instancja może zarządzać pulą i wydzielać połączenia efektywnie.

Jedna klasa, jedna instancja, jeden punkt dostępu — prosta idea z dalekosiężnymi konsekwencjami.

Najważniejsze cechy wzorca

CechaCo to oznacza
Pojedyncza instancjaKlasa zaprojektowana tak, by mieć tylko jedną instancję, często przez prywatny konstruktor.
Globalny punkt dostępuStatyczna metoda (np. getInstance()) daje jednolity sposób dostępu do instancji.
Opóźniona inicjalizacjaInstancja tworzona przy pierwszym żądaniu, co może zmniejszyć koszty startu.
Zarządzanie stanemCentralne miejsce dla fragmentu globalnego stanu, np. ustawień aplikacji.

Praktyczne przypadki użycia

Najlepiej stosować Singletony tam, gdzie zasób jest z natury unikalny:

  • Usługi logowania: jeden logger zapewnia spójność logów.
  • Zarządzanie konfiguracją: jedno źródło ustawień aplikacji.
  • Interfejs do sprzętu: pojedynczy adapter do urządzenia zapobiega konfliktom.

Zalety i wady

Waga porównująca uporządkowane wzorce projektowe z złożonym kodem.

Zalety

  • Globalny punkt dostępu upraszcza użycie w różnych modułach.
  • Opóźniona inicjalizacja oszczędza zasoby przy starcie.
  • Redukcja duplikacji zapobiega konfliktom między instancjami.

Wady

  • Ścisłe powiązanie ukrywa zależności i utrudnia testowanie.
  • Współdzielony mutowalny stan może prowadzić do trudnych do znalezienia błędów.
  • Ukryte skutki uboczne: metody nie ujawniają zależności w sygnaturze.

Wpływ na testowanie i powiązania

Singletony komplikują testy jednostkowe przez trwały, globalny stan. Mockowanie takiego obiektu może być niewygodne, dlatego nowoczesne zespoły często wybierają wstrzykiwanie zależności (DI), które czyni zależności jawymi i łatwymi do zastąpienia podczas testów3.

Równoważenie kompromisów

Rozważ wygodę kontra utrzymywalność. W projektach legacy najbezpieczniejsza jest stopniowa refaktoryzacja w kierunku DI: zachowujesz dotychczasowe zachowanie, jednocześnie redukując ukryte powiązania.

Implementacja Singletona w TypeScript

Diagram pokazujący ConfigManager z kłódką, demonstrujący wzorzec Singleton używając getInstance w TypeScript.

Poniżej praktyczny, typowany przykład Singletona w TypeScript. Sekret to prywatny konstruktor i statyczna metoda strażniczka.

Przykład: ConfigManager

class ConfigManager {
  private static instance: ConfigManager;
  private settings: Map<string, any> = new Map();

  private constructor() {
    console.log("Initializing ConfigManager instance...");
    this.settings.set("API_URL", "https://api.example.com");
    this.settings.set("TIMEOUT", 5000);
  }

  public static getInstance(): ConfigManager {
    if (!ConfigManager.instance) {
      ConfigManager.instance = new ConfigManager();
    }
    return ConfigManager.instance;
  }

  public get(key: string): any {
    return this.settings.get(key);
  }
}

// const config = new ConfigManager(); // Błąd: konstruktor jest prywatny

Wykorzystanie w serwisie

class ApiService {
  private apiUrl: string;

  constructor() {
    const config = ConfigManager.getInstance();
    this.apiUrl = config.get("API_URL");
    console.log(`ApiService initialized with API URL: ${this.apiUrl}`);
  }

  public fetchData(): void {
    console.log(`Fetching data from ${this.apiUrl}...`);
    // Logika pobierania danych
  }
}

console.log("Application starting...");
const service1 = new ApiService();
service1.fetchData();
const service2 = new ApiService();
console.log("Application finished.");

Po uruchomieniu zobaczysz inicjalizację ConfigManagera tylko raz, co potwierdza działanie Singletona.

Dlaczego Singletony mają złą reputację

Singletony są atrakcyjne przez prostotę, ale często prowadzą do ukrytych zależności, globalnego mutowalnego stanu i problemów z testowaniem. Kiedy klasa cicho sięga po instancję globalną, ukrywa zależność, która powinna być jawna w konstruktorze, co utrudnia zrozumienie i testowanie kodu3.

Problemy ze współbieżnością

Stanowe Singletony mogą prowadzić do warunków wyścigu; rozważ licznik sesji, gdzie dwa jednoczesne żądania inkrementują ten sam licznik bez synchronizacji — takie błędy są zależne od czasu i trudne do odtworzenia7.

Testowanie

Globalny stan może wyciekać między testami, a mockowanie Singletona bywa nieporęczne. DI upraszcza testy, bo mocki można wstrzykiwać bezpośrednio.

Nowoczesne alternatywy

Zamiast programowego Singletona, rozważ wstrzykiwanie zależności i kontenery IoC. DI czyni zależności jawymi i poprawia testowalność oraz modułowość.

DI kontra Singleton

Przykład: zamiast pobierać Singleton w konstruktorze, przyjmij menedżera konfiguracji przez konstruktor.

interface IConfigManager { get(key: string): any; }

class ApiService {
  private apiUrl: string;

  constructor(config: IConfigManager) {
    this.apiUrl = config.get("API_URL");
  }
}

Teraz ApiService zależy od kontraktu, co pozwala w testach przekazać fikcyjny obiekt.

Kontenery IoC

Frameworki takie jak NestJS czy biblioteki jak InversifyJS zarządzają tworzeniem i cyklem życia obiektów, pozwalając uzyskać zachowanie podobne do Singletona bez ukrytych powiązań45.

Refaktoryzacja Singletonów w kodzie legacy

Postępuj inkrementalnie: zidentyfikuj wywołania getInstance(), zdefiniuj interfejs, refaktoryzuj konsumentów na DI i w punkcie kompozycji przekaż jedną instancję lub pozwól kontenerowi DI zarządzać cyklem życia.

Kroki

  1. Zidentyfikuj i zlokalizuj wszystkie wywołania Singletona.
  2. Zdefiniuj interfejs publicznych metod implementacji.
  3. Refaktoryzuj konsumentów, aby akceptowali interfejs przez konstruktor.
  4. Utwórz instancję w punkcie kompozycji lub użyj kontenera DI.

FAQ — Szybkie pytania i odpowiedzi

P: Kiedy Singleton jest właściwy? A: Gdy zasób jest naprawdę unikalny i bezstanowy, np. centralny logger lub adapter do sprzętu. Preferuj DI, gdy to możliwe.

P: Jak testować kod używający Singletona? A: Wprowadź interfejs, refaktoryzuj konsumentów, aby akceptowali zależność i wstrzykuj stuby/mocki. Migrację wykonuj stopniowo.

P: Czy klasa statyczna to to samo co Singleton? A: Nie. Klasa statyczna ma tylko statyczne członkowskie elementy. Singleton to jedna instancja, którą można przekazywać jako obiekt i implementować interfejsy.

Q&A — Krótkie odpowiedzi na najczęstsze wątpliwości

Czy Singleton zawsze szkodzi? Nie. Ma sens dla naprawdę unikalnych zasobów, ale zwykle DI oferuje większą elastyczność i lepszą testowalność.

Jak ograniczyć ryzyko podczas używania Singletona? Używaj leniwej inicjalizacji, dbaj o bezpieczeństwo współbieżne i udokumentuj punkt kompozycji, skąd instancja jest udostępniana.

Jak zacząć refaktoryzację w dużym projekcie? Zmapuj użycia Singletonów, wprowadź interfejsy i refaktoryzuj pojedynczych konsumentów na DI, utrzymując kompatybilność przez punkty kompozycji.

Dalsze lektury i zasoby

Zobacz także: Wzorce projektowe, Wstrzykiwanie zależności oraz oficjalny podręcznik TypeScript: TypeScript Handbook.

1.
Martin Fowler, “Singleton,” Bliki, https://martinfowler.com/bliki/Singleton.html
2.
3.
Mark Seemann, “Singletons Are Pathological Liars,” https://blog.ploeh.dk/2010/07/28/SingletonsArePathologicalLiar/
4.
NestJS Documentation, “Dependency Injection,” https://docs.nestjs.com/fundamentals/injection
5.
InversifyJS Documentation, https://inversify.io/
6.
Stack Overflow, “Developer Survey,” https://insights.stackoverflow.com/survey
7.
Wikipedia, “Race condition,” https://en.wikipedia.org/wiki/Race_condition
← Back to blog
🙋🏻‍♂️

AI pisze kod.
Ty sprawiasz, że przetrwa.

W erze przyspieszenia AI czysty kod to nie tylko dobra praktyka — to różnica między systemami, które się skalują, a bazami kodu, które zapadają się pod własnym ciężarem.

Singleton w TypeScript: kompletny przewodnik | Clean Code Guy