Wzór singletona - Singleton pattern

Klasy schemat przykładowego z Singleton.

W inżynierii oprogramowania , wzorzec singleton jest wzorzec projektowania oprogramowania , które ogranicza instancji o klasie do jednego „single” instancji. Jest to przydatne, gdy do koordynowania działań w systemie potrzebny jest dokładnie jeden obiekt.

Termin pochodzi z matematycznego pojęcia singletona .

Przegląd

Wzorzec projektowy singleton jest jednym z dwudziestu trzech dobrze znanych wzorców projektowych „Gang Czterech”, które opisują, jak rozwiązywać powtarzające się problemy projektowe w celu zaprojektowania elastycznego i wielokrotnego użytku oprogramowania zorientowanego obiektowo w celu ułatwienia jego implementacji, zmiany, testować i ponownie wykorzystywać obiekty.

Wzorzec projektowy singleton rozwiązuje problemy, umożliwiając mu:

  • Upewnij się, że klasa ma tylko jedną instancję
  • Łatwy dostęp do jedynej instancji klasy
  • Kontroluj jego instancję
  • Ogranicz liczbę instancji
  • Uzyskaj dostęp do zmiennej globalnej

Wzorzec projektowy singleton opisuje, jak rozwiązać takie problemy:

  • Ukryć konstruktorów tej klasy .
  • Zdefiniuj publiczną operację statyczną ( getInstance()), która zwraca jedyną instancję klasy.

Zasadniczo wzorzec singletona wymusza na nim odpowiedzialność za zapewnienie, że jest on tworzony tylko raz. Ukryty konstruktor — zadeklarowany privatelub — protectedzapewnia, że ​​nigdy nie można utworzyć wystąpienia klasy spoza klasy. Dostęp do publicznej operacji statycznej można uzyskać za pomocą nazwy klasy i nazwy operacji, np Singleton.getInstance(). .

Typowe zastosowania

Logowanie to klasyczny przykład singletona.

Krytyka

Krytycy uważają, że singleton jest antywzorcem , ponieważ jest często używany w scenariuszach, w których nie jest korzystny, ponieważ często wprowadza niepotrzebne ograniczenia w sytuacjach, w których klasa singletona nie byłaby korzystna, tym samym wprowadzając stan globalny do aplikacji.

Co więcej, ponieważ do singletona można uzyskać dostęp z dowolnego miejsca bez konieczności używania jakichkolwiek parametrów, wszelkie zależności nie byłyby od razu widoczne dla programistów. W związku z tym programiści musieliby znać „wewnętrzne działanie kodu, aby prawidłowo go używać”.

Singletony naruszają również zasadę single-responsibility , ponieważ nie tylko są odpowiedzialne za normalne zadanie singletona, ale muszą również zapewnić, że tylko jeden jest tworzony; zauważ, że opiera się to na „klasycznej” definicji singletona, w której jest on odpowiedzialny za wymuszanie własnej unikalności poprzez, na przykład, getInstance()metodę statyczną .

Inną wadą jest to, że singletony są trudne do przetestowania, ponieważ przenoszą stan globalny na czas trwania programu. W szczególności dlatego, że testowanie jednostkowe wymaga luźno powiązanych klas w celu odizolowania tego, co jest testowane. Dodatkowo, gdy dana klasa wchodzi w interakcję z singletonem, ta klasa i singleton stają się ściśle powiązane , co uniemożliwia testowanie samej klasy bez testowania singletona.

Realizacje

Implementacje wzorca singletona muszą:

  • Upewnij się, że istnieje tylko jedna instancja klasy singleton; oraz
  • Zapewnij globalny dostęp do tej instancji.

Zazwyczaj odbywa się to poprzez:

Instancja jest zwykle przechowywana jako prywatna zmienna statyczna ; instancja jest tworzona, gdy zmienna jest inicjowana, w pewnym momencie przed pierwszym wywołaniem metody statycznej.

Jawa

Implementacja singletona w Javie :

public class Coin {

    private static final int ADD_MORE_COIN = 10;
    private int coin;
    private static Coin instance = new Coin(); // eagerly loads the singleton

    private Coin(){
        // private to prevent anyone else from instantiating
    }

    public static Coin getInstance() {
        return instance;
    }

    public int getCoin() {
        return coin;
    }

    public void addMoreCoin() {
        coin += ADD_MORE_COIN;
    }

    public void deductCoin() {
        coin--;
    }
}
public final class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

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

Inicjalizacja leniwa

Implementacja singletona może używać inicjalizacji z opóźnieniem , w której instancja jest tworzona przy pierwszym wywołaniu metody statycznej. Jeśli metoda statyczna może być wywoływana z wielu wątków jednocześnie, może być konieczne podjęcie środków, aby zapobiec sytuacji wyścigu, która może spowodować utworzenie wielu wystąpień. Poniżej przedstawiono bezpieczną wątkowo implementację, używającą inicjalizacji leniwej z podwójnie sprawdzanym blokowaniem , napisaną w Javie. Aby uniknąć narzutów związanych z synchronizacją przy jednoczesnym zachowaniu leniwej inicjalizacji z bezpieczeństwem wątków, preferowanym podejściem w Javie jest użycie idiomu posiadacza inicjalizacji na żądanie .

public final class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}

Pyton

class Singleton:
    __instance = None

    def __new__(cls, *args):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls, *args)
        return cls.__instance

C++

Od C++11 inicjalizacja statycznej zmiennej lokalnej jest bezpieczna wątkowo i występuje po pierwszym wywołaniu. Jest to bezpieczniejsza alternatywa niż przestrzeń nazw lub zmienna statyczna o zasięgu klasy.

class Singleton {
 public:
  static Singleton& GetInstance() {
    // Allocate with `new` in case Singleton is not trivially destructible.
    static Singleton* singleton = new Singleton();
    return *singleton;
  }

 private:
  Singleton() = default;

  // Delete copy/move so extra instances can't be created/moved.
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(Singleton&&) = delete;
};

C#

Implementacja singletona w C# :

public sealed class Singleton
{
    public static Singleton Instance { get; } = new Singleton();

    private Singleton() { }
}

W C# można również używać klas statycznych do tworzenia singletonów, gdzie sama klasa jest singletonem.

public static class Singleton
{
    public static MyOtherClass Instance { get; } = new MyOtherClass();
}

Jedność

Singletony mogą być przydatnym narzędziem podczas programowania w Unity dzięki sposobowi tworzenia instancji klas. Ta metoda jest bardziej preferowana niż ukrywanie konstruktorów , ponieważ możliwe jest utworzenie instancji obiektu z ukrytym konstruktorem w Unity. Aby zapobiec nadpisaniu instancji, należy sprawdzić, czy instancja jest null. Jeśli nie jest null, GameObjectskrypt zawierający naruszający prawo powinien zostać zniszczony.

Jeśli inne komponenty są zależne od singletona, kolejność wykonywania skryptu powinna zostać zmodyfikowana tak, aby komponent definiujący singleton był wykonywany jako pierwszy.

class Singleton : MonoBehaviour
{
    public static Singleton Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null && Instance != this) {
            Destroy(this.gameObject);
        } else {
            Instance = this;
        }
    }
}

Zauważ, że możliwe jest również zaimplementowanie singletona poprzez usunięcie tylko naruszającego skryptu, a nie GameObjectsamego, poprzez wywołanie Destroy(this).

Strzałka

Implementacja Singletona w Dart :

class Singleton {
    Singleton._();
    static Singleton get instance => Singleton._();
}

PHP

Implementacja singletona w PHP :

class Singleton
{
    private static $instance = null;

    private function __construct() {}

    public static function getInstance(): self
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }
}

Kotlin

Słowo kluczowe obiektu Kotlin deklaruje klasę singletona

object Coin {
    private var coin: Int = 0

    fun getCoin():Int {
        return coin
    }

    fun addCoin() {
        coin += 10
    }

    fun deductCoin() {
        coin--
    }
}

Pascal

Bezpieczna wątkowo implementacja singletona w Pascalu :

unit SingletonPattern;

interface

type
  TTest = class sealed
  strict private
    FCreationTime: TDateTime;
  public
    constructor Create;
    property CreationTime: TDateTime read FCreationTime;
  end;

function GetInstance: TTest;

implementation

uses
  SysUtils
  , SyncObjs
  ;

var
  FCriticalSection: TCriticalSection;
  FInstance: TTest;

function GetInstance: TTest;
begin
  FCriticalSection.Acquire;
  try
    if not Assigned(FInstance) then
      FInstance := TTest.Create;

    Result := FInstance;
  finally
    FCriticalSection.Release;
  end;
end;

constructor TTest.Create;
begin
  inherited Create;
  FCreationTime := Now;
end;

initialization
  FCriticalSection := TCriticalSection.Create;

finalization
  FreeAndNil(FCriticalSection);

end.

Stosowanie:

procedure TForm3.btnCreateInstanceClick(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to 5 do
    ShowMessage(DateTimeToStr(GetInstance.CreationTime));
end;

Zobacz też

Bibliografia

Zewnętrzne linki