Wzór singletona - Singleton pattern
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 private
lub — protected
zapewnia, ż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
- Streszczenie fabryka , metoda fabryki , budowniczy i prototypowe wzory mogą używać pojedynczych.
- Obiekty elewacyjne są często singletonami, ponieważ wymagany jest tylko jeden obiekt elewacyjny.
- Obiekty stanowe są często singletonami.
- Singletony są często preferowane od zmiennych globalnych, ponieważ:
- Nie zanieczyszczają globalnej przestrzeni nazw (lub, w językach z zagnieżdżonymi przestrzeniami nazw, zawierającej je przestrzeni nazw) niepotrzebnymi zmiennymi.
- Pozwalają na leniwą alokację i inicjalizację, podczas gdy zmienne globalne w wielu językach zawsze zużywają zasoby.
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:
- Deklarowanie wszystkich konstruktorów klasy jako prywatnych ; oraz
- Zapewnienie statycznej metody, która zwraca odwołanie do instancji.
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, GameObject
skrypt 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 GameObject
samego, 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
- Pełny artykuł „ Objaśnienie wzorca Java Singleton ”
- Cztery różne sposoby implementacji singletona w Javie " Sposoby implementacji singletona w Javie "
- Fragment książki: Implementacja wzorca Singleton w C# autorstwa Jona Skeeta
- Singleton w Microsoft Patterns & Practices Developer Center
- Artykuł IBM „ Double-checked lock and the Singleton pattern ” autorstwa Petera Haggara
- Geary, David (25 kwietnia 2003). „Jak poruszać się po zwodniczo prostym wzorze Singletona” . Wzorce projektowe Java. JavaŚwiat . Źródło 2020-07-21 .
- Google Singleton Detector (analizuje kod bajtowy Java w celu wykrywania singletonów)