Singleton jest wzorcem projektowym, który umożliwia istnienie tylko jednej instancji danej klasy bez możliwości tworzenia nowych przez użycie słowa new ( w większości języków programowania).  Wydaje ci się to dziwne ? Otóż czasami jest to bardzo przydatna funkcjonalność która umożliwi prawidłowe działanie zaprojektowanej aplikacji.

Po co używać wzorzec singleton ?

Pomyśl, że potrzebujesz w swojej aplikacji tylko jednej instancji danej klasy. Powiedzmy, że chciałbyś aby wszystkie inne obiekty w danym widoku twej aplikacji (może to być na przykład też strona www sklepu internetowego czy bloga) korzystały z zasobów i stanu tylko tego i jednego obiektu. Singleton w bezpośrednim tłumaczeniu na język polski to : singiel . To nam już wiele mówi.

Przykład zastosowania wzorca Singleton w programowaniu

Na przykład obiektem takim może być instancja klasy odpowiedzialnej za łączenie się bazą danych MySQL na twojej stronie www. Pomyśl, że musisz odpytać bazę danych o kategorie dostępne w sklepie, później o produkty w danej kategorii , następnie za produkty “hot sale” , wyprzedawane , o kilka najnowszych komentarzy , o dane zalogowane użytkownika itp itd. Będzie może ze 20 połączeń z bazą danych co ? I to tylko dla jednej strony i dla jednego odwiedzającego sklep. A co jak masz tysiąc odwiedzających w danej chwili na stronie ? Ciągłe łączenie z bazą danych nie jest tu najlepszym rozwiązaniem gdyż :

  • każdorazowe łączenie z bazą danych trwa chwilę ( a masz tych połączeń kilka[dziesiąt])
  • baza danych może mieć (i pewnie ma) limit jednorazowych połączeń w danej chwili

Problem taki (czy podobny) można rozwiązać stosując wzorzec singleton. Ten wzorzec projektowy pozwoli ci utworzyć tylko jedną instancję danej klasy i gdy jakaś część kodu będzie chciała utworzyć ponownie ten obiekt, utworzenie jego nie będzie możliwe a jedynie otrzyma stworzoną wcześniej instancję która będzie miała już jakiś , wcześniej ustawiony stan.

Jakie problemy rozwiązuje Wzorzec Singleton ?

Wzorzec Singleton jak i inne wzorce projektowe został stworzony aby rozwiązać jakiś często napotykany problem lub problemy podczas budowania aplikacji. Singleton jest wzorcem, który rozwiązuje dwie sprawy :

  • Zapewnie istnienie tylko jednej instancji danej klasy mimo prób tworzenia nowych obiektów danej klasy  – to o czym pisałem wyżej. Singleton jest napisany tak, aby uniemożliwił tworzenie nowych instancji danej klasy i zawsze zwracał tą samą  (wcześniej utworzoną lub tworzoną po raz pierwszy i jedyny) instancję tej klasy. Poniżej w sekcji kodu zobaczysz jak to jest doskonale skonstruowane poprzez stworzenie prywatnego konstruktora klasy Wzorca Singleton.

 

  • Umożliwia dostęp do tej instancji z poziomu globalnego aplikacji – z Singletona możesz skorzystać w dowolnym miejscu w aplikacji. Ponieważ zawiera zmienne i metody statyczne to nie trzeba tworzyć (no i w sumie się nie da) nowych instancji tejże klasy. Wywołując utworzony Singleton korzystasz ze statycznej metody która zwraca już wcześniej stworzoną instancję singletona tam gdzie tego potrzebujesz. Za każdym razem otrzymasz ten sam obiekt.

 

Zasada tworzenia i działania wzorca singleton

Niezależnie jakiego języka programowania używasz, jest kilka podstawowych kroków, które musisz wykonać aby utworzyć wzorzec singleton.

  1. Ograniczyć (zablokować) dostęp do tworzenia nowych instancji (głownie przez użycie słowa new) poprzez ustawienie domyślnego konstruktora klasy prywatnym
  2. Utworzenie statycznej metody w klasie singletona która będzie rozpoznawała czy jest to pierwsze użycie (call) singletona w aplikacji i w tym momencie tworzyła instancję tejże klasy poprzez wywołanie prywatnego konstruktora , przypisanie instancji do zmiennej statycznej i zwrócenie jej przy każdorazowym wywołaniu tejże metody statycznej. (W praktyce jest to prostsze niż się wydaje)

Singleton w Javie – przykład 1

Jak napisałem wcześniej w zasadzie tworzenia i działania wzorca singleton, musimy a) zablokować konstruktor b) stworzyć statyczną metodę tworzącą i zwracającą instancję klasy przypisaną do zmiennej statycznej klasy

public class MySingletonClass {
      
      private static MySingletonClass INSTANCE;

      private MySingletonClass () { } // blokujemy konstruktor 

      public static MySingletonClass getInstance() {
          if (INSTANCE == null) {
              INSTANCE = new MySingletonClass();
          }
          return INSTANCE;
      }
}

Powyżej podałem klasyczny i podstawowy sposób tworzenia wzorca singleton w Javie. Jednak spotkać się możesz także (najczęściej w nowszych aplikacjach) z użyciem ENUM jako budulca wzorca singleton w Javie. Użycie enuma do tworzenia singletona jest bezpieczniejsze, gdyż zabezpiecza go przed problemami w wielowątkowości.

Enum Singleton w Javie – przykład 2

public enum MySingletonNameHandler {

    INSTANCE;

    private String name = "";

    public String getName(){
        return this.name;
    }

    public void setName(String newName){
        this.name = newName;
    }

    public boolean saveName(){
        System.out.println("Saving name to file ...");
        return true;
    }

    public boolean loadName(){
        System.out.println("Loading name from the file ...");
        return true;
    }

}

Użycie klasy MySingletonNameHandler

public class SingletonTest {
    
    private static MySingletonNameHandler mySingletonNameHandler;

    public static void main(String[] args) {
        mySingletonNameHandler = MySingletonNameHandler.INSTANCE;
        mySingletonNameHandler.setName("Mario");
        String name = mySingletonNameHandler.getName();
        System.out.println(name); // "Mario"

        MySingletonNameHandler anotherHandler = MySingletonNameHandler.INSTANCE;
        System.out.println(anotherHandler.getName()); // "Mario"
    }
}

Uruchamiając powyższy kod i jest klasę testową, widać wyraźnie że mimo tego iż próbowaliśmy utworzyć nową instancję klasy MySingletonNameHandler i przypisać ją do zmiennej anotherHandler w wyniku wykonania metody getName został wyświetlone imię “Mario” czyli takie, jakie ustawiliśmy naszemu singletonowi w instancji mySingletonNameHandler .

Minusem użycia enuma do budowy singletona jest między innymi to, że nie można jej rozszerzać po innych klasach. Może nie jest to bardzo istotne ale na pewno są przypadki, które tego potrzebują.

Singleton a wielowątkowość

Aby ochronić kod klasycznego singletona (przykład 1) przed nieprawidłowościami powstałymi na skutek wielowątkowości można wykorzystać ten sam kod ale dokonać małej modyfikacji poprzez dodanie klauzuli synchronized.

public class MySingletonClass {

    private static volatile MySingletonClass INSTANCE;

    private MySingletonClass () { } 

    public static MySingletonClass getInstance() {
        if(INSTANCE == null){
            synchronized (MySingletonClass.class){
                if (INSTANCE == null) {
                    INSTANCE = new MySingletonClass();
                }
            }
        }
        return INSTANCE;
    }
}

Dostawienie klauzuli synchronized spowoduje że jeżeli dwa (lub więcej) wątki przejdą przez pierwszy warunek IF , zostaną zakolejkowane przed synchronized a kolejny poziom warunku IF będzie czuwał nad tym aby nie została stworzona kolejna instancja poprzez keyword new.


Seweryn

Cześć. Mam na imię Seweryn i jestem związany z IT już od lat 90. Jest to moje hobby i stała, która towarzyszy mi i chyba będzie do końca życia. Najbardziej interesują mnie takie dziedziny jak programowanie, bazy danych, analiza danych, web design. Posiadam kilka certyfikatów rozpoznawalnych w "przemyśle IT". M. in. Certified Oracle Java 8 Associate, Certified Amazon Cloud Practitioner, Certified Python Programmer. Mam także Professional Diploma in Digital Marketing oraz Certyfikat Data Analysis for Management z LSE : London School of Economics and Political Science

0 Komentarzy

Dodaj komentarz