Menu
Coddy logo textTech

Wzorzec Singleton

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 65 z 87.

Wzorzec Singleton to kreacyjny wzorzec projektowy, który zapewnia, że w całej aplikacji istnieje tylko jedna instancja danej klasy, a jednocześnie udostępnia globalny punkt dostępu do tej instancji. Przydaje się w przypadku zasobów, które powinny być współdzielone, takich jak menedżery konfiguracji, pule połączeń czy usługi logowania.

Ten wzorzec działa przez ustawienie konstruktora jako prywatnego, co uniemożliwia bezpośrednie tworzenie instancji. Zamiast tego dostęp do pojedynczej instancji kontroluje metoda statyczna:

public class DatabaseConnection {
    private static DatabaseConnection instance;
    
    // Prywatny konstruktor uniemożliwia bezpośrednie tworzenie instancji
    private DatabaseConnection() {
        System.out.println("Connection created");
    }
    
    // Globalny punkt dostępu
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
    
    public void query(String sql) {
        System.out.println("Executing: " + sql);
    }
}

Każde wywołanie getInstance() zwraca ten sam obiekt. Instancja jest tworzona dopiero przy pierwszym żądaniu. Nazywa się to inicjalizacją leniwą:

DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();

System.out.println(db1 == db2);  // true - ta sama instancja

Kluczowe elementy wzorca Singleton to: pole private static przechowujące instancję, konstruktor private oraz metoda public static, która zwraca instancję. To połączenie gwarantuje, że niezależnie od tego, ile razy poprosisz o obiekt, zawsze otrzymasz ten sam.

challenge icon

Wyzwanie

Łatwy

Stwórzmy menedżera konfiguracji aplikacji, korzystając ze wzorca Singleton! Menedżer konfiguracji zadba o to, by wszystkie części aplikacji korzystały z tych samych ustawień, niezależnie od tego, ile razy będą prosić o dostęp do nich.

Rozmieścisz kod w dwóch plikach:

  • AppConfig.java: Utwórz klasę Singleton zarządzającą ustawieniami konfiguracji aplikacji. Twoja klasa AppConfig powinna zawierać:

    Prywatne pole statyczne przechowujące jedyną instancję.

    Prywatne pole typu String o nazwie appName oraz prywatne pole typu int o nazwie maxUsers.

    Prywatny konstruktor, który inicjuje appName wartością "MyApplication", a maxUsers wartością 100, a następnie wypisuje AppConfig initialized.

    Publiczną metodę statyczną getInstance(), która tworzy instancję tylko wtedy, gdy jeszcze nie istnieje (inicjalizacja leniwa), a następnie ją zwraca.

    Metodę setAppName(String name), która aktualizuje nazwę aplikacji.

    Metodę setMaxUsers(int max), która aktualizuje maksymalną liczbę użytkowników.

    Metodę displaySettings(), która wypisuje App: [appName], Max Users: [maxUsers].

  • Main.java: Zademonstruj, że Singleton działa prawidłowo! Otrzymasz dwa dane wejściowe: nową nazwę aplikacji (String) oraz nową maksymalną liczbę użytkowników (liczba całkowita).

    Najpierw pobierz instancję AppConfig i zapisz ją w zmiennej o nazwie config1. Wywołaj displaySettings(), aby wyświetlić wartości domyślne.

    Następnie zaktualizuj ustawienia, używając wartości wejściowych, przez wywołanie metod setAppName i setMaxUsers na config1.

    Pobierz kolejną instancję i zapisz ją w config2. Wywołaj displaySettings() na config2, aby pokazać, że odzwierciedla ona zmiany wprowadzone za pośrednictwem config1.

    Na koniec wypisz, czy oba odwołania wskazują na ten sam obiekt: Same instance: [true/false], porównując config1 == config2.

Otrzymasz kolejno dwa dane wejściowe: nową nazwę aplikacji (String) oraz nową maksymalną liczbę użytkowników (liczba całkowita).

To wyzwanie pokazuje główną zaletę wzorca Singleton: niezależnie od tego, ile razy poprosisz o konfigurację, zawsze otrzymasz tę samą instancję ze współdzielonym stanem!

Spróbuj swoich sił

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Wczytaj dane wejściowe
        String newAppName = scanner.nextLine();
        int newMaxUsers = scanner.nextInt();
        
        // TODO: Pobierz pierwszą instancję AppConfig i zapisz ją w config1
        
        // TODO: Wywołaj displaySettings() na config1, aby wyświetlić wartości domyślne
        
        // TODO: Zaktualizuj ustawienia za pomocą setAppName i setMaxUsers na config1
        
        // TODO: Pobierz kolejną instancję i zapisz ją w config2
        
        // TODO: Wywołaj displaySettings() na config2, aby wykazać, że zmiany są współdzielone
        
        // TODO: Wypisz, czy obie referencje wskazują na ten sam obiekt:
        // "Same instance: [true/false]", używając config1 == config2
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Java online