Szukaj…


Wprowadzenie

Singleton to klasa, która ma tylko jedną instancję. Więcej informacji na temat wzorca projektowego Singleton można znaleźć w temacie Singleton w znaczniku Wzory projektowe .

Enum Singleton

Java SE 5
public enum Singleton {
    INSTANCE;

    public void execute (String arg) {
        // Perform operation here 
    }
}

Wyliczenia mają prywatnych konstruktorów, są ostateczne i zapewniają odpowiednią maszynę do serializacji. Są również bardzo zwięzłe i leniwie inicjowane w sposób bezpieczny dla wątków.

JVM zapewnia gwarancję, że wartości wyliczenia nie zostaną utworzone więcej niż raz, co daje wzorzec singletonu wyliczenia bardzo silną obronę przed atakami odbicia.

Wzorzec wyliczania nie chroni przed innymi programistami fizycznie dodającymi więcej elementów do kodu źródłowego. W związku z tym, jeśli wybierzesz ten styl implementacji dla swoich singletonów, konieczne jest, aby bardzo wyraźnie udokumentować, że do tych wyliczeń nie należy dodawać żadnych nowych wartości.

Jest to zalecany sposób implementacji wzorca singletonu, jak wyjaśnił Joshua Bloch w Effective Java.

Singleton bezpieczny z podwójnym ryglowaniem

Ten typ Singleton jest bezpieczny dla wątków i zapobiega niepotrzebnemu blokowaniu po utworzeniu instancji Singleton.

Java SE 5
public class MySingleton {

    // instance of class
    private static volatile MySingleton instance = null;

    // Private constructor
    private MySingleton() {
        // Some code for constructing object
    }

    public static MySingleton getInstance() {
        MySingleton result = instance;
        
        //If the instance already exists, no locking is necessary
        if(result == null) {
            //The singleton instance doesn't exist, lock and check again
            synchronized(MySingleton.class) {
                result = instance;
                if(result == null) {
                    instance = result = new MySingleton();
                }
            }
        }
        return result;
    }
}

Należy to podkreślić - w wersjach wcześniejszych niż Java SE 5 powyższa implementacja jest niepoprawna i należy jej unikać. Nie można poprawnie zaimplementować podwójnie sprawdzonego blokowania w Javie przed Javą 5.

Singleton bez użycia Enum (chętna inicjalizacja)

public class Singleton {    

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Można argumentować, że ten przykład jest faktycznie leniwą inicjalizacją. Sekcja 12.4.1 specyfikacji języka Java stanowi:

Klasa lub typ interfejsu T zostanie zainicjowany bezpośrednio przed pierwszym wystąpieniem któregokolwiek z poniższych:

  • T jest klasą i tworzona jest instancja T.
  • T jest klasą i wywoływana jest metoda statyczna zadeklarowana przez T.
  • Pole statyczne zadeklarowane przez T. jest przypisane
  • Wykorzystywane jest pole statyczne zadeklarowane przez T, a pole nie jest zmienną stałą
  • T jest klasą najwyższego poziomu i wykonywana jest instrukcja assert leksykalnie zagnieżdżona w T.

Dlatego tak długo, jak w klasie nie ma innych pól statycznych ani metod statycznych, instancja Singleton nie zostanie zainicjowana, dopóki metoda getInstance() zostanie wywołana po raz pierwszy.

Bezpieczna inicjalizacja leniwa przy użyciu klasy posiadacza | Wdrożenie Billa Pugh Singletona

public class Singleton {
    private static class InstanceHolder {
        static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private Singleton() {}
}

To inicjuje zmienną INSTANCE przy pierwszym wywołaniu Singleton.getInstance() , wykorzystując gwarancje bezpieczeństwa wątków języka do statycznej inicjalizacji bez konieczności dodatkowej synchronizacji.

Ta implementacja jest również znana jako wzorzec singletona Billa Pugha. [Wiki]

Rozszerzanie singletonu (dziedziczenie singletonu)

W tym przykładzie klasa podstawowa Singleton udostępnia metodę getMessage() , która zwraca "Hello world!" wiadomość.

Podklasy UppercaseSingleton i LowercaseSingleton zastępują metodę getMessage (), aby zapewnić odpowiednią reprezentację komunikatu.

//Yeah, we'll need reflection to pull this off.
import java.lang.reflect.*;

/*
Enumeration that represents possible classes of singleton instance.
If unknown, we'll go with base class - Singleton.
*/
enum SingletonKind {
    UNKNOWN,
    LOWERCASE,
    UPPERCASE
}

//Base class
class Singleton{

    /*
    Extended classes has to be private inner classes, to prevent extending them in 
    uncontrolled manner.
     */
    private class UppercaseSingleton extends Singleton {

        private UppercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toUpperCase();
        }
    }

    //Another extended class.
    private class LowercaseSingleton extends Singleton
    {
        private LowercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toLowerCase();
        }
    }

    //Applying Singleton pattern
    private static SingletonKind kind = SingletonKind.UNKNOWN;

    private static Singleton instance;

    /*
    By using this method prior to getInstance() method, you effectively change the
    type of singleton instance to be created.
     */
    public static void setKind(SingletonKind kind) {
        Singleton.kind = kind;
    }

    /*
    If needed, getInstance() creates instance appropriate class, based on value of
    singletonKind field.
     */
    public static Singleton getInstance() 
        throws  NoSuchMethodException, 
                IllegalAccessException, 
                InvocationTargetException, 
                InstantiationException {

        if(instance==null){
            synchronized (Singleton.class){
                if(instance==null){
                    Singleton singleton = new Singleton();
                    switch (kind){
                        case UNKNOWN:

                            instance = singleton;
                            break;

                        case LOWERCASE:

                            /*
                             I can't use simple

                             instance = new LowercaseSingleton();

                             because java compiler won't allow me to use
                             constructor of inner class in static context,
                             so I use reflection API instead.

                             To be able to access inner class by reflection API,
                             I have to create instance of outer class first.
                             Therefore, in this implementation, Singleton cannot be
                             abstract class.
                             */

                            //Get the constructor of inner class.
                            Constructor<LowercaseSingleton> lcConstructor =
                                    LowercaseSingleton.class.getDeclaredConstructor(Singleton.class);

                            //The constructor is private, so I have to make it accessible.
                            lcConstructor.setAccessible(true);

                            // Use the constructor to create instance.
                            instance = lcConstructor.newInstance(singleton);

                            break;

                        case UPPERCASE:

                            //Same goes here, just with different type
                            Constructor<UppercaseSingleton> ucConstructor =
                                    UppercaseSingleton.class.getDeclaredConstructor(Singleton.class);
                            ucConstructor.setAccessible(true);
                            instance = ucConstructor.newInstance(singleton);
                    }
                }
            }
        }
        return instance;
    }

    //Singletons state that is to be used by subclasses
    protected String message;

    //Private constructor prevents external instantiation.
    private Singleton()
    {
        message = "Hello world!";
    }

    //Singleton's API. Implementation can be overwritten by subclasses.
    public String getMessage() {
        return message;
    }
}

//Just a small test program
public class ExtendingSingletonExample {

    public static void main(String args[]){

        //just uncomment one of following lines to change singleton class

        //Singleton.setKind(SingletonKind.UPPERCASE);
        //Singleton.setKind(SingletonKind.LOWERCASE);

        Singleton singleton = null;
        try {
            singleton = Singleton.getInstance();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        System.out.println(singleton.getMessage());
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow