I. Wzorce kreacyjne
1. Singleton
2. Budowniczy
3. Prototyp
4. Fabryka
5. Fabryka abstrakcyjna
II. Wzorce strukturalne
1. Adapter
2. Most
3. Kompozyt
4. Dekorator
5. Fasada
6. Pyłek
7. Pełnomocnik
III. Wzorce czynnościowe
1. Łańcuch zobowiązań
2. Polecenie
3. Interpreter
4. Iterator
5. Mediator
6. Pamiątka
7. Obserwator
8. Stan
9. Strategia
10. Metoda szablonowa
11. Odwiedzający

Singleton - wzorzec projektowy (design pattern) - java

1. Cel:
Wzorzec projektowy Singleton ma za zadanie dostarczyć tylko jedną instancje klasy.
Zapewnia jeden globalny punkt dostępu do tej instancji.

2. Problem:
Singleton używany jest na przykład gdy chcemy zapewnić kontrole dostępu do dzielonego zasobu takiego jak baza danych lub plik itp.

3. Rozwiązanie:
Prywatny konstruktor który zapobiega użyciu słowa kluczowego new, dla instancji klasy.
Stworzenie metody statycznej zwracającej stworzony tylko jeden obiekt.

4. Diagram klas klasy Singleton:


5. Implementacja:
Mamy kilka rodzajów implementacji singleton-a.

a) Jednowątkowy:
  1. package pl.edu.java.singleton;
  2.  
  3. public final class SingletonNonThreadSafe {
  4.     private static SingletonNonThreadSafe instance;
  5.  
  6.     private SingletonNonThreadSafe() {
  7.     }
  8.  
  9.     public static SingletonNonThreadSafe getInstance() {
  10.         if (instance == null) {
  11.             instance = new SingletonNonThreadSafe();
  12.         }
  13.         return instance;
  14.     }
  15. }
  16.  
b) gorliwe ładowanie (Eagar loading), jest bezpieczne dla wielu wątków
  1. package pl.edu.java.singleton;
  2.  
  3. public final class SingletonEager {
  4.    
  5.     private static final SingletonEager instance = new SingletonEager();
  6.    
  7.     private SingletonEager(){}
  8.  
  9.     public static SingletonEager getInstance(){
  10.         return instance;
  11.     }
  12. }
  13.  
c) synchronizowana metoda
  1. package pl.edu.java.singleton;
  2.  
  3. public class SingletonSynchMethod {
  4.  
  5.     private static SingletonSynchMethod instance;
  6.    
  7.     private SingletonSynchMethod(){}
  8.    
  9.     public static synchronized SingletonSynchMethod getInstance(){
  10.         if(instance == null){
  11.             instance = new SingletonSynchMethod();
  12.         }
  13.         return instance;
  14.     }
  15.    
  16. }
  17.  
d) synchronizowany blok
  1. package pl.edu.java.singleton;
  2.  
  3. public class SingletonSynchBlock {
  4.  
  5.     private static SingletonSynchBlock instance;
  6.    
  7.         public static SingletonSynchBlock getInstance(){
  8.             if(instance == null){
  9.                 synchronized (SingletonSynchBlock.class) {
  10.                     if(instance == null){
  11.                         instance = new SingletonSynchBlock();
  12.                         }
  13.                     }
  14.             }
  15.             return instance;
  16.        }
  17.   }
e) ale pomimo tego że mamy blok synchronizowany zmienna instance może się zmienić w linii 11, a nie zostać zauważona w linii 8.
Aby temu zapobiec dodajemy na zmienną instance słowo kluczowe volatile - mówi ono:
"Uważaj - bo może ona zmienić wartość nawet wtedy, gdy się nie spodziewasz!"
to zapobiegnie że nie zostanie zauważona zmiana w jednym wątku gdy zmiana dokonuje się w drugim.
Technicznie rzecz biorąc, zmienna będzie pobierana z pamięci komputera nie z cache-u procesora
  1. package pl.edu.java.singleton;
  2.  
  3. public class SingletonSynchBlockVolatile {
  4.  
  5.     private static volatile SingletonSynchBlockVolatile instance;
  6.    
  7.         public static SingletonSynchBlockVolatile getInstance(){
  8.             if(instance == null){
  9.                 synchronized (SingletonSynchBlockVolatile.class) {
  10.                     if(instance == null){
  11.                         instance = new SingletonSynchBlockVolatile();
  12.                         }
  13.                     }
  14.             }
  15.             return instance;
  16.        }
  17.   }
f) Singleton Bill-a Pugh-a
Wydaje się najlepszym podejściem, ponieważ nie jest ładowana do pamięci do póki nie zostanie wywołana metoda statyczne getInstance();
  1. package pl.edu.java.singleton;
  2.  
  3. public class SingletonBillPugh {
  4.  
  5.     private SingletonBillPugh(){}
  6.    
  7.     private static class SingletonHelper{
  8.         private static final SingletonBillPugh INSTANCE = new SingletonBillPugh();
  9.     }
  10.    
  11.     public static SingletonBillPugh getInstance(){
  12.         return SingletonHelper.INSTANCE;
  13.     }
  14. }
  15.  
g) Ale ciągle jest możliwe jest tworzenie instancji przez reflekcje:
  1. public class SingletonRef {
  2.     public static void main(String[] args) {
  3.        Singleton s1 = Singleton.getInstance();
  4.        Singleton s2 = Singleton.getInstance();
  5.        Class clazz = Class.forName("pl.org.java.singleton.Singleton");
  6.        Constrictor<Singleton> cs = clazz.getDeclaredConstructor();
  7.        cs.setAccessable(true);
  8.        Singleton s3 = cs.newInstance();
  9.        print("s1", s1);
  10.        print("s2", s2);
  11.        print("s3", s3);
  12.     }
  13.        
  14.     private static void print(String name, Singleton s) {
  15.             System.out.println(String.format("Instancja o nazwie: %s, posiada hashcode: ", name, s.hashcode()));
  16.     }
  17. }
  18.  
Aby temu zapobiec możemy oddac w konstruktorze:
  1.         private Singleton() {
  2.                 if(instance != null) {
  3.                         throw new RuntimeException("Instance exist! You can't create second!");
  4.                 }
  5.         }

h) Serializacja/deserializacja: wynikają z niej problemy z inną wersją instancji
Klasa testowa
  1. package pl.edu.java.singleton;
  2.  
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7.  
  8. public class SingletonSerializableTest {
  9.     public static void main(String[] args) throws Exception {
  10.         Singleton s1 = Singleton.getInstance();
  11.         Singleton s2 = Singleton.getInstance();
  12.  
  13.         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c:\\matmad\\obj1"));
  14.         oos.writeObject(s2);
  15.         ObjectInputStream ois = new ObjectInputStream(new FileInputStream("c:\\matmad\\obj1"));
  16.  
  17.         Singleton s3 = (Singleton)ois.readObject();
  18.  
  19.         print("s1", s1);
  20.         print("s2", s2);
  21.         print("s3", s3);
  22.  
  23.     }
  24.     private static void print(String name, Singleton s) {
  25.         System.out.println(String.format("Instancja o nazwie: %s, posiada hashcode: %d", name, s.hashCode()));
  26.     }
  27. }
Rozwiązaniem tego jest metoda w klasie singletonu:
  1. privateObjectreadResolve() {
  2.         return instance;
  3. }
Klasa singleton-u będzie wyglądać następująco
  1. package pl.edu.java.singleton;
  2.  
  3. import java.io.Serializable;
  4.  
  5. class Singleton implements Serializable {
  6.     private static Singleton instance = new Singleton();
  7.     private Singleton() {
  8.     }
  9.  
  10.     public static Singleton getInstance() {
  11.         return instance;
  12.     }
  13.     private ObjectreadResolve() {
  14.         return instance;
  15.     }
  16.  
Wada tego rozwiązania jest taka, że wywołanie deserializacji nie nadpisze naszego singletonu.
Więc jeżeli będziemy mieć zmienną value = 1 i zapiszemy do pliku, następnie zmienimy value = 2,
następnie deserializujemy obiekt Singleton zapisany do pliku, to zmienna zapisana w pliku czyli value = 2 nie nadpisze
wartości którą zmieniliśy w naszej klasie, czyli pomimo deserializacji value = 1, obiekt dalej będzie równy 2.
i) Zapobieganie clone-owania:
  1. package pl.edu.java.singleton;
  2. public class SingletonClone implements Cloneable {
  3.     private static SingletonClone instance = new SingletonClone();
  4.     private SingletonClone() {
  5.     }
  6.  
  7.     public static SingletonClone getInstance() {
  8.         return instance;
  9.     }
  10.         public Object clone() throws CloneNotSupportedException {
  11.                 if ( instance != null ) {
  12.                         throw new CloneNotSupportedException();
  13.                 }
  14.                 return super.clone();
  15.         }
  16. }
j) Joshua Bloch zaleca tworzenie singletonu na podstawie typu enum:
  1. package pl.edu.java.singleton;
  2.  
  3. public enum SingletonEnum {
  4.     INSTANCE;
  5.  
  6.     int value;
  7.  
  8.     public int getValue() {
  9.         return value;
  10.     }
  11.     public void setValue(int value) {
  12.         this.value = value;
  13.     }
  14. }
  15.  
6. Zastosowanie w kodzie java:

java.lang.Runtime#getRuntime() - zastowowany SingletonEager:
java.lang.System#getSecurityManager() - zastosowana metoda synchronizowana SingletonSynchMethod,
ale wewnątrz innej metody. Zmienna ze słowem kluczowym volatile;
java.awt.Desktop#getDesktop() - zastosowana metoda synchronizowana SingletonSynchMethod
created by cv.java.org.pl © 2023 All Rights Reserved.