Szukaj…


Uwagi

Wzorzec projektowy Singleton jest czasem uważany za „ wzór antypoślizgowy ”. Wynika to z faktu, że ma pewne problemy. Musisz sam zdecydować, czy uważasz, że należy go użyć. Temat ten był kilkakrotnie omawiany na StackOverflow.

Zobacz: http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons

Singleton (C #)

Singletony są używane do zapewnienia, że tworzona jest tylko jedna instancja obiektu. Singleton umożliwia utworzenie tylko jednej instancji samego siebie, co oznacza, że kontroluje jego tworzenie. Singleton jest jednym z wzorców projektowych Gang of Four i jest wzorem kreacyjnym .

Wzorzec Singleton bezpieczny dla wątków

public sealed class Singleton
{
    private static Singleton _instance;
    private static object _lock = new object();
 
    private Singleton()
    {
    }
 
    public static Singleton GetSingleton()
    {
        if (_instance == null)
        {
             CreateSingleton();
        }

        return _instance;
    }

    private static void CreateSingleton()
    {
        lock (_lock )
        {
            if (_instance == null)
            {
                 _instance = new Singleton();
            }
        }
    }
}

Jon Skeet zapewnia następującą implementację leniwego, bezpiecznego dla wątków singletona:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());
    
    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
} 

Singleton (Java)

Singletony w Javie są bardzo podobne do C #, ponieważ oba języki są zorientowane obiektowo. Poniżej znajduje się przykład klasy singleton, w której tylko jedna wersja obiektu może być żywa podczas życia programu (Zakładając, że program działa na jednym wątku)

public class SingletonExample {

    private SingletonExample() { }

    private static SingletonExample _instance;

    public static SingletonExample getInstance() {

        if (_instance == null) {
            _instance = new SingletonExample();
        }
        return _instance;
    }
}

Oto bezpieczna dla wątków wersja tego programu:

public class SingletonThreadSafeExample {

    private SingletonThreadSafeExample () { }

    private static volatile SingletonThreadSafeExample _instance;

    public static SingletonThreadSafeExample getInstance() {
        if (_instance == null) {
                createInstance();
        }
        return _instance;
    }

    private static void createInstance() {
        synchronized(SingletonThreadSafeExample.class) {
            if (_instance == null) {
                _instance = new SingletonThreadSafeExample();
            }
        }
    }
}

Java ma również obiekt o nazwie ThreadLocal , który tworzy pojedynczą instancję obiektu w wątku według wątku. Może to być przydatne w aplikacjach, w których każdy wątek potrzebuje własnej wersji obiektu

public class SingletonThreadLocalExample {

    private SingletonThreadLocalExample () { }

    private static ThreadLocal<SingletonThreadLocalExample> _instance = new ThreadLocal<SingletonThreadLocalExample>();

    public static SingletonThreadLocalExample getInstance() {
        if (_instance.get() == null) {
            _instance.set(new SingletonThreadLocalExample());
        }
        return _instance.get();
    }
}

Oto także implementacja Singleton z enum (zawierająca tylko jeden element):

public enum SingletonEnum {
    INSTANCE;
    // fields, methods
}

Każda implementacja klasy Enum zapewnia, że istnieje tylko jedna instancja każdego elementu.

Wzór Bill Pugh Singleton

Bill Pugh Singleton Pattern jest najczęściej stosowanym podejściem w klasie Singleton, ponieważ nie wymaga synchronizacji

public class SingletonExample {

    private SingletonExample(){}
    
    private static class SingletonHolder{
        private static final SingletonExample INSTANCE = new SingletonExample();
    }
    
    public static SingletonExample getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

przy użyciu prywatnej wewnętrznej klasy statycznej uchwyt nie jest ładowany do pamięci, dopóki ktoś nie wywoła metody getInstance. Rozwiązanie Billa Pugha jest bezpieczne dla wątków i nie wymaga synchronizacji.


Więcej przykładów singletonów Java znajduje się w temacie Singletons pod znacznikiem dokumentacji Java.

Singleton (C ++)

Zgodnie z Wiki : w inżynierii oprogramowania wzorzec singletonu jest wzorcem projektowym, który ogranicza tworzenie instancji klasy do jednego obiektu.

Jest to wymagane do utworzenia dokładnie jednego obiektu do koordynowania działań w całym systemie.

class Singleton
{
    // Private constructor so it can not be arbitrarily created.
    Singleton()
    {}
    // Disable the copy and move
    Singleton(Singleton const&)            = delete;
    Singleton& operator=(Singleton const&) = delete;
  public:

    // Get the only instance
    static Singleton& instance()
    {
        // Use static member.
        // Lazily created on first call to instance in thread safe way (after C++ 11)
        // Guaranteed to be correctly destroyed on normal application exit.
        static Singleton _instance;

        // Return a reference to the static member.
        return _instance;
    }
};

Lazy Singleton praktyczny przykład w java

Prawdziwe przypadki użycia wzoru Singleton;

Jeśli tworzysz aplikację klient-serwer, potrzebujesz pojedynczej instancji ConnectionManager , która zarządza cyklem życia połączeń klienta.

Podstawowe interfejsy API w ConnectionManager:

registerConnection : Dodaj nowe połączenie do istniejącej listy połączeń

closeConnection : Zamknij połączenie ze zdarzenia wywołanego przez klienta lub serwer

broadcastMessage : Czasami musisz wysłać wiadomość do wszystkich subskrybowanych połączeń klienckich.

Nie zapewniam pełnej implementacji kodu źródłowego, ponieważ przykład będzie bardzo długi. Na wysokim poziomie kod będzie taki.

import java.util.*;
import java.net.*;

/* Lazy Singleton - Thread Safe Singleton without synchronization and volatile constructs */
final class  LazyConnectionManager {
    private Map<String,Connection> connections = new HashMap<String,Connection>();
    private LazyConnectionManager() {}
    public static LazyConnectionManager getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazyConnectionManager INSTANCE = new LazyConnectionManager();
    }

    /* Make sure that De-Serailzation does not create a new instance */
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
    public void registerConnection(Connection connection){
        /* Add new connection to list of existing connection */
        connections.put(connection.getConnectionId(),connection);
    }
    public void closeConnection(String connectionId){
        /* Close connection and remove from map */
        Connection connection = connections.get(connectionId);
        if ( connection != null) {
            connection.close();
            connections.remove(connectionId);
        }
    }
    public void broadcastMessage(String message){
        for (Map.Entry<String, Connection> entry : connections.entrySet()){
            entry.getValue().sendMessage(message);            
        }
    }    
}

Przykładowa klasa serwera:

class Server implements Runnable{
    ServerSocket socket;
    int id;
    public Server(){
        new Thread(this).start();
    }
    public void run(){
        try{
            ServerSocket socket = new ServerSocket(4567);
            while(true){
                Socket clientSocket = socket.accept();
                ++id;
                Connection connection = new Connection(""+ id,clientSocket);
                LazyConnectionManager.getInstance().registerConnection(connection);    
                LazyConnectionManager.getInstance().broadcastMessage("Message pushed by server:");
            }
        }catch(Exception err){
            err.printStackTrace();
        }
    }
    
}

Inne praktyczne przypadki użycia singletonów:

  1. Zarządzanie globalnymi zasobami, takimi jak ThreadPool, ObjectPool, DatabaseConnectionPool itp.
  2. Scentralizowane usługi, takie jak Logging danych aplikacji z różnymi poziomami rejestrowania, takie jak DEBUG,INFO,WARN,ERROR itp
  3. Globalny RegistryService Usługa, w której różne usługi są rejestrowane w centralnym komponencie podczas uruchamiania. Ta globalna usługa może działać jako Facade aplikacji

Przykład C #: Wielowątkowy Singleton

Inicjalizacja statyczna jest odpowiednia w większości sytuacji. Gdy aplikacja musi opóźnić tworzenie instancji, użyć niestandardowego konstruktora lub wykonać inne zadania przed instancją i pracować w środowisku wielowątkowym, potrzebujesz innego rozwiązania. Istnieją jednak przypadki, w których nie można polegać na środowisku uruchomieniowym wspólnego języka w celu zapewnienia bezpieczeństwa wątków, tak jak w przykładzie inicjalizacji statycznej. W takich przypadkach należy użyć określonych możliwości językowych, aby zapewnić utworzenie tylko jednej instancji obiektu w obecności wielu wątków. Jednym z bardziej powszechnych rozwiązań jest użycie idiomu Double-Check Locking [Lea99], aby nie dopuścić do tworzenia oddzielnych wątków w tym samym czasie przez nowe wystąpienia singletonu.

Następująca implementacja pozwala na wejście tylko jednego wątku do obszaru krytycznego, który blok blokowy identyfikuje, gdy nie utworzono jeszcze instancji Singleton:

using System;

public sealed class Singleton {    
   private static volatile Singleton instance;    
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance    {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }    
  } 
}

Takie podejście zapewnia, że tworzona jest tylko jedna instancja i tylko wtedy, gdy jest ona potrzebna. Zmienna jest również deklarowana jako niestabilna, aby zapewnić, że przypisanie do zmiennej instancji zostanie zakończone przed uzyskaniem dostępu do zmiennej instancji. Wreszcie, to podejście wykorzystuje instancję syncRoot do blokowania, zamiast blokowania samego typu, aby uniknąć zakleszczenia.

Takie podejście do blokowania podwójnej kontroli rozwiązuje problemy dotyczące współbieżności wątków, unikając blokady wyłącznej w każdym wywołaniu metody właściwości Instance. Pozwala także opóźnić tworzenie instancji do momentu pierwszego dostępu do obiektu. W praktyce aplikacja rzadko wymaga tego rodzaju implementacji. W większości przypadków wystarcza statyczna inicjalizacja.

Odniesienie: MSDN

Podziękowanie

[Gamma95] Gamma, Helm, Johnson i Vlissides. Wzorce projektowe: elementy oprogramowania obiektowego wielokrotnego użytku. Addison-Wesley, 1995.

[Lea99] Lea, Doug. Programowanie równoległe w Javie, wydanie drugie. Addison-Wesley, 1999.

[Sells03] Sprzedaje, Chris. „Sealed Sucks”. aktualności sellsbrothers.com. Dostępne pod adresem : http://www.sellsbrothers.com/news/showTopic.aspx?ixTopic=411 .

Singleton (PHP)

Przykład z phptherightway.com

<?php
class Singleton
{
    /**
     * @var Singleton The reference to *Singleton* instance of this class
     */
    private static $instance;
    
    /**
     * Returns the *Singleton* instance of this class.
     *
     * @return Singleton The *Singleton* instance.
     */
    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }
        
        return static::$instance;
    }

    /**
     * Protected constructor to prevent creating a new instance of the
     * *Singleton* via the `new` operator from outside of this class.
     */
    protected function __construct()
    {
    }

    /**
     * Private clone method to prevent cloning of the instance of the
     * *Singleton* instance.
     *
     * @return void
     */
    private function __clone()
    {
    }

    /**
     * Private unserialize method to prevent unserializing of the *Singleton*
     * instance.
     *
     * @return void
     */
    private function __wakeup()
    {
    }
}

class SingletonChild extends Singleton
{
}

$obj = Singleton::getInstance();
var_dump($obj === Singleton::getInstance());             // bool(true)

$anotherObj = SingletonChild::getInstance();
var_dump($anotherObj === Singleton::getInstance());      // bool(false)

var_dump($anotherObj === SingletonChild::getInstance()); // bool(true)

Wzór singletonu (ogólnie)

Uwaga: singleton jest wzorem.
Ale uważał również za anty-wzór.

Przed użyciem należy rozważyć stosowanie singletonu. Zazwyczaj są lepsze alternatywy.

Główny problem z singletonem jest taki sam jak problem ze zmiennymi globalnymi. Wprowadzają zewnętrzny globalny stan zmienny. Oznacza to, że funkcje wykorzystujące singleton nie są zależne wyłącznie od parametrów wejściowych, ale także od stanu singletonu. Oznacza to, że testowanie może być poważnie zagrożone (trudne).

Problemy z singletonami można złagodzić, stosując je w połączeniu ze wzorcami tworzenia; tak aby można było kontrolować początkowe utworzenie singletonu.



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