Singleton mönster - Singleton pattern

Image
Ett klassdiagram som exemplifierar singletonmönstret.

Inom mjukvaruteknik är singletonmönstret ett mjukvarudesignmönster som begränsar instansering av en klass till en "enda" instans. Detta är användbart när exakt ett objekt behövs för att koordinera åtgärder i hela systemet.

Termen kommer från det matematiska konceptet singleton .

Översikt

Singleton-mönstret är ett av de tjugotre tre välkända "Gang of Four" -mönstren som beskriver hur man löser återkommande designproblem för att designa flexibel och återanvändbar objektorienterad programvara i syfte att göra det enklare att implementera, ändra, testa och återanvända objekt.

Singleton -mönstret löser problem genom att låta det:

  • Se till att en klass bara har en instans
  • Få enkelt tillgång till den enda instansen av en klass
  • Kontrollera dess instans
  • Begränsa antalet instanser
  • Få tillgång till en global variabel

Singleton -mönstret beskriver hur man löser sådana problem:

  • Dölj konstruktörerna i klassen .
  • Definiera en offentlig statisk operation ( getInstance()) som returnerar den enda instansen av klassen.

I huvudsak tvingar singletonmönstret att ansvara för att det bara instanseras en gång. En dold konstruktör - deklarerad privateeller - protectedförsäkrar att klassen aldrig kan instanseras utanför klassen. Den public static operation kan nås med hjälp av klassnamnet och drift namn, till exempel Singleton.getInstance().

Vanliga användningsområden

Loggning är ett klassiskt exempel på en singleton.

Kritik

Kritiker anser att singleton är ett antimönster genom att det ofta används i scenarier där det inte är fördelaktigt eftersom det ofta inför onödiga restriktioner i situationer där en singleton-klass inte skulle vara till nytta, och därigenom inför global status i en applikation.

Eftersom en singleton dessutom kan nås var som helst av någonting utan att behöva använda några parametrar, skulle alla beroenden inte vara direkt synliga för utvecklare. Följaktligen skulle utvecklare behöva känna till "kodens inre funktion för att kunna använda den korrekt".

Singletons bryter också mot principen om ett enda ansvar , eftersom de inte bara är ansvariga för singletons normala uppgift, det måste också se till att endast en är instanserad; Observera att detta bygger på en "klassisk" singleton -definition där den är ansvarig för att upprätthålla sin egen unikhet genom till exempel en statisk getInstance()metod.

En annan nackdel är att singletons är svåra att testa eftersom de bär globalt tillstånd under programmets varaktighet. Specifikt eftersom enhetstestning kräver löst kopplade klasser för att isolera det som testas. Dessutom, när en viss klass interagerar med en singleton, blir den klassen och singleton tätt kopplade , vilket gör det omöjligt att testa klassen på egen hand utan att också testa singleton.

Implementeringar

Implementeringarna av singletonmönstret måste:

  • Se till att bara en instans av singleton -klassen någonsin existerar; och
  • Ge global åtkomst till den instansen.

Normalt görs detta av:

Instansen lagras vanligtvis som en privat statisk variabel ; instansen skapas när variabeln initialiseras, någon gång innan den statiska metoden först anropas.

Java

Singleton -implementering i 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;
    }
}

Lat initialisering

En singleton -implementering kan använda lat initialisering , där instansen skapas när den statiska metoden först anropas. Om den statiska metoden kan anropas från flera trådar samtidigt kan det behöva vidtas åtgärder för att förhindra tävlingsförhållanden som kan leda till att flera instanser skapas. Följande är en trådsäker implementering med lat initialisering med dubbelkontrollerad låsning , skriven i Java. För att undvika synkroniseringsomkostnader samtidigt som lat initialisering med trådsäkerhet hålls är det föredragna tillvägagångssättet i Java att använda id-id för initialisering på begäran .

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

Pytonorm

class Singleton:
    __instance = None

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

C ++

Från och med C ++ 11 är initialisering av statisk lokal variabel trådsäker och inträffar efter den första anropet. Detta är ett säkrare alternativ än en namnrymd eller klassisk omfattande statisk variabel.

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 -implementering i C# :

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

    private Singleton() { }
}

I C# kan du också använda statiska klasser för att skapa singletons, där själva klassen är singleton.

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

Enhet

Singletons kan vara ett användbart verktyg när de utvecklas med Unity tack vare hur klasser instanseras. Denna metod föredras framför konstruktörsgömning , eftersom det är möjligt att instantiera ett objekt med en dold konstruktör i Unity. För att förhindra att en instans skrivs över måste en kontroll utföras för att säkerställa att instansen är det null. Om det inte är null GameObjectbör det innehållande det kränkande skriptet förstöras.

Om andra komponenter är beroende av singleton, bör exekveringsordern för scriptet ändras för att säkerställa att komponenten som definierar singleton körs först.

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

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

Observera att det också är möjligt att implementera en singleton genom att bara ta bort det kränkande skriptet, inte GameObjectsjälva, genom att istället ringa Destroy(this).

Pil

Singleton -implementering i Dart :

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

PHP

Singleton -implementering i 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

Kotlin objekt nyckelord förklarar en singleton klass

object Coin {
    private var coin: Int = 0

    fun getCoin():Int {
        return coin
    }

    fun addCoin() {
        coin += 10
    }

    fun deductCoin() {
        coin--
    }
}

Pascal

En trådsäker implementering av en singleton i 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.

Användande:

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

Se även

Referenser

externa länkar