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 instancjaKluczowe 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.
Wyzwanie
ŁatwyStwó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 klasaAppConfigpowinna zawierać:Prywatne pole statyczne przechowujące jedyną instancję.
Prywatne pole typu String o nazwie
appNameoraz prywatne pole typu int o nazwiemaxUsers.Prywatny konstruktor, który inicjuje
appNamewartością"MyApplication", amaxUserswartością100, a następnie wypisujeAppConfig 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 wypisujeApp: [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ę
AppConfigi zapisz ją w zmiennej o nazwieconfig1. WywołajdisplaySettings(), aby wyświetlić wartości domyślne.Następnie zaktualizuj ustawienia, używając wartości wejściowych, przez wywołanie metod
setAppNameisetMaxUsersnaconfig1.Pobierz kolejną instancję i zapisz ją w
config2. WywołajdisplaySettings()naconfig2, aby pokazać, że odzwierciedla ona zmiany wprowadzone za pośrednictwemconfig1.Na koniec wypisz, czy oba odwołania wskazują na ten sam obiekt:
Same instance: [true/false], porównującconfig1 == 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
}
}
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 obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatnościPoćwicz samodzielnie: Kompilator Java online