Singleton-Muster - Singleton pattern

Ein Klassendiagramm, das das Singleton-Muster veranschaulicht.

In der Softwareentwicklung ist das Singleton-Muster ein Software-Entwurfsmuster, das die Instanziierung einer Klasse auf eine "einzelne" Instanz beschränkt. Dies ist nützlich, wenn genau ein Objekt benötigt wird, um Aktionen im gesamten System zu koordinieren.

Der Begriff stammt aus dem mathematischen Konzept eines Singletons .

Überblick

Das Singleton Design Pattern ist eines der dreiundzwanzig bekannten "Gang of Four" Design Patterns , die beschreiben, wie wiederkehrende Designprobleme gelöst werden können, um flexible und wiederverwendbare objektorientierte Software zu entwerfen mit dem Ziel, die Implementierung, Änderung, Testen und Wiederverwenden von Objekten.

Das Singleton-Entwurfsmuster löst Probleme, indem es Folgendes ermöglicht:

  • Stellen Sie sicher, dass eine Klasse nur eine Instanz hat
  • Einfach auf die einzige Instanz einer Klasse zugreifen
  • Kontrollieren Sie seine Instanziierung
  • Beschränken Sie die Anzahl der Instanzen
  • Auf eine globale Variable zugreifen

Das Singleton-Design-Pattern beschreibt, wie solche Probleme gelöst werden können:

  • Blenden Sie die Konstruktoren der Klasse aus .
  • Definieren Sie eine öffentliche statische Operation ( getInstance()), die die einzige Instanz der Klasse zurückgibt.

Im Wesentlichen zwingt das Singleton-Muster es dazu, dafür zu sorgen, dass es nur einmal instanziiert wird. Ein versteckter Konstruktor – deklariert privateoder – stellt protectedsicher, dass die Klasse niemals von außerhalb der Klasse instanziiert werden kann. Auf die öffentliche statische Operation kann über den Klassennamen und den Operationsnamen zugegriffen werden, zB Singleton.getInstance().

Häufige Verwendungen

Die Protokollierung ist ein klassisches Beispiel für einen Singleton.

Kritik

Kritiker halten das Singleton insofern für ein Anti-Muster , als es häufig in Szenarien verwendet wird, in denen es nicht von Vorteil ist, da es oft unnötige Einschränkungen in Situationen einführt, in denen eine Singleton-Klasse nicht von Vorteil wäre, wodurch ein globaler Zustand in eine Anwendung eingeführt wird.

Da auf ein Singleton von überall aus zugegriffen werden kann, ohne Parameter verwenden zu müssen, wären außerdem alle Abhängigkeiten für Entwickler nicht sofort sichtbar. Folglich müssten Entwickler "das Innenleben des Codes kennen, um ihn richtig zu verwenden".

Singletons verstoßen auch gegen das Single-Responsibility-Prinzip , da sie nicht nur für die normale Aufgabe des Singletons verantwortlich sind, sondern auch dafür sorgen müssen, dass nur einer instanziiert wird; Beachten Sie, dass dies auf einer "klassischen" Singleton-Definition beruht, bei der sie für die Durchsetzung ihrer eigenen Eindeutigkeit beispielsweise durch eine statische getInstance()Methode verantwortlich ist.

Ein weiterer Nachteil besteht darin, dass Singletons schwer zu testen sind, da sie für die Dauer des Programms einen globalen Status tragen. Insbesondere, weil Unit-Tests lose gekoppelte Klassen erfordern, um zu isolieren, was getestet wird. Wenn eine bestimmte Klasse mit einem Singleton interagiert, werden diese Klasse und der Singleton außerdem eng gekoppelt , sodass es unmöglich ist, die Klasse alleine zu testen, ohne auch den Singleton zu testen.

Implementierungen

Implementierungen des Singleton-Musters müssen:

  • Stellen Sie sicher, dass immer nur eine Instanz der Singleton-Klasse existiert; und
  • Stellen Sie globalen Zugriff auf diese Instanz bereit.

In der Regel geschieht dies durch:

Die Instanz wird normalerweise als private statische Variable gespeichert ; die Instanz wird erstellt, wenn die Variable initialisiert wird, bevor die statische Methode zum ersten Mal aufgerufen wird.

Java

Singleton-Implementierung in Java :

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;
    }
}

Faule Initialisierung

Eine Singleton-Implementierung kann Lazy Initialization verwenden , wobei die Instanz erstellt wird, wenn die statische Methode zum ersten Mal aufgerufen wird. Wenn die statische Methode könnte aus mehreren aufgerufen werden Threads gleichzeitig, können Maßnahmen ergriffen werden müssen , um zu verhindern Rennbedingungen , die bei der Schaffung von mehreren Instanzen führen könnten. Das Folgende ist eine Thread-sichere Implementierung, die eine verzögerte Initialisierung mit doppelt geprüftem Sperren verwendet und in Java geschrieben ist. Um den Synchronisations-Overhead zu vermeiden und gleichzeitig eine verzögerte Initialisierung mit Thread-Sicherheit beizubehalten, besteht der bevorzugte Ansatz in Java darin, das Initialization-on-Demand-Halter-Idiom zu verwenden .

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;
    }
}

Python

class Singleton:
    __instance = None

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

C++

Ab C++11 ist die Initialisierung statischer lokaler Variablen threadsicher und erfolgt nach dem ersten Aufruf. Dies ist eine sicherere Alternative als ein Namespace oder eine statische Variable mit Klassenbereich.

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#

Singleton-Implementierung in C# :

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

    private Singleton() { }
}

In C# können Sie auch statische Klassen verwenden, um Singletons zu erstellen, wobei die Klasse selbst der Singleton ist.

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

Einheit

Singletons können dank der Instanziierung von Klassen ein nützliches Werkzeug bei der Entwicklung mit Unity sein . Diese Methode wird dem Verbergen von Konstruktoren vorgezogen , da es möglich ist, ein Objekt mit einem versteckten Konstruktor in Unity zu instanziieren. Um zu verhindern, dass eine Instanz überschrieben wird, muss überprüft werden, ob die Instanz null. Wenn es nicht null ist, sollte das GameObjectSkript , das das anstößige Skript enthält, zerstört werden.

Wenn andere Komponenten vom Singleton abhängig sind, sollte die Ausführungsreihenfolge des Skripts geändert werden, um sicherzustellen, dass die Komponente, die das Singleton definiert, zuerst ausgeführt wird.

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

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

Beachten Sie, dass es auch möglich ist, ein Singleton zu implementieren, indem Sie nur das anstößige Skript entfernen, nicht das GameObjectselbst, indem Sie stattdessen aufrufen Destroy(this).

Pfeil

Singleton-Implementierung in Dart :

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

PHP

Singleton-Implementierung in 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

Das Kotlin- Objektschlüsselwort deklariert eine Singleton-Klasse

object Coin {
    private var coin: Int = 0

    fun getCoin():Int {
        return coin
    }

    fun addCoin() {
        coin += 10
    }

    fun deductCoin() {
        coin--
    }
}

Pascal

Eine threadsichere Implementierung eines Singletons in Pascal :

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.

Verwendungszweck:

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

Siehe auch

Verweise

Externe Links