Top Banner
THE SINGLETON DESIGN PATTERN by A. Buniak
36

T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

Dec 16, 2015

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

THE SINGLETON DESIGN PATTERNby A. Buniak

Page 2: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ВСТУП + ПОВТОРЕННЯ

Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр, і забезпечує глобальний доступ до нього

Не дивлячись на легку постановку задачі, з’являються значні труднощі при реалізації

2/36

Page 3: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ЩО МИ ВИВЧИМО

Риси , що відрізняють Singleton від звичайної глобальної змінної

Основні ідіоми мови С++ для підтримки сінглтонів

Забезпечення унікальності сінглтону Видалення сінглтона, керування

тривалістю життя сінглтона, звернення до нього після видалення.

Багатопотоковість

3/36

Page 4: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ПОСТАНОВКА ЗАДАЧІ

Три класи-одинака : Keyboard, Display, Log Log генерує повідомлення про помилки

(якщо їх немає, Log взагалі не створюється).

Класу Log надсилаються всі помилки, які виникають при створенні та видаленні класів

Keyboard та Display.

4/36

Page 5: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ПЕРШІ ІДЕЇ

Лише статичні функції і статичні данісlass Singleton{

public: static void DoJob1();

…private: static int pole1; …

} 5/36

Page 6: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

STATIC DATA + STATIC FUNCTIONS != SINGLETON

Лише статичні функції і статичні данісlass Singleton{

public: static void DoJob1();

…private: static int pole1; …

}1. Статичні функції не можуть бути віртуальними

2. Труднощі з ініціалізацією і видаленням об’єктів.

6/36

Page 7: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

БІЛЬШ ЗВИЧНЕ РІШЕННЯ (ПОВТОРЕННЯ)

// Singleton.h

class Singleton

{

public:

static Singleton& Instance() //Єдина точка доступу, відсилка більш безпечніша

{

if (!pInstance_)

pInstance_ = new Singleton;

return pInstance_;

}

... operations ...

private:

Singleton(); // забороняємо явне створення нового об’єкту

Singleton(const Singleton&); // забороняємо створення копії Singleton-у

static Singleton* pInstance_; // єдиний екземпляр

Singleton& operator=(const Singleton&);// оператор = не має логіки для одинака

~Singleton();//закриваємо деструктор

};

// Singleton.cpp

Singleton* Singleton::pInstance_ = 0;

7/36

Page 8: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ВАЖЛИВА ДЕТАЛЬ

// Singleton.h

class Singleton

{

public:

private:

static Singleton* pInstance_;

//vs static Singleton instance_;

};

// Singleton.cpp

Singleton* Singleton::pInstance_ = 0;

//vs Singleton Singleton::instance ;

//динамічна vs статична ініціалізація 8/36

Проблемний приклад:

// SomeFile.cpp#include "Singleton.h"int global = Singleton::Instance()->DoSomething();

Page 9: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ВИДАЛЕННЯ SINGLETON

Memory leak має місце якщо конструктор одинака запросить необмежену к-сть ресурсів

Єдиний вихід – видаляти об’єкт класу Singleton при виході з програми

The Meyers Singleton Singleton& Singleton::Instance(){

static Singleton obj;return obj;

}9/36

Page 10: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ПСЕВДОКОД

Singleton& Singleton::Instance()

{

extern void __ConstructSingleton(void* memory);

extern void __DestroySingleton();

static bool __initialized = false;

static char __buffer[sizeof(Singleton)];

if (!__initialized)

{

__ConstructSingleton(__buffer);

atexit(__DestroySingleton);

__initialized = true;

}

return *reinterpret_cast<Singleton *>(__buffer);

}10/36

Page 11: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

THE DEAD REFERENCE PROBLEMКонтр приклад:1) Keyboard::Instance() // створено без помилок//atexit(deleteKeyboard);2) Display::Instance() //трапилася помилка при створенні…Display(){ … if (error) Log::Instance().add (“Display creation error”); //atexit(deleteLog);}3) deleteLog(); //ок4) deleteKeyboard()// трапилася помилка при видаленні{if (error) Log::Instance().add (“Keyboard deleting error”);// Log::Instance() - dead reference, завдяки принципу LIFO}

11/36

Page 12: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ХОЧА Б ОБРОБИТИclass Singleton{…private:bool destroyed_;virtual ~Singleton(){pInstance_ = 0;destroyed_ = true;}} // Singleton.cpp…bool Singleton::destroyed_ = false; 12/

36

Page 13: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

class Singleton{ … private: // Create a new Singleton and store a // pointer to it in pInstance_ static void Create(); { static Singleton theInstance; pInstance_ = &theInstance; } // Gets called if dead reference detected static void OnDeadReference() { throw std::runtime_error("Dead Reference Detected"); }

}

13/36

Page 14: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

class Singleton{

public:Singleton& Instance() { if (!pInstance_) {

// Check for dead referenceif (destroyed_) {

OnDeadReference(); }

else { // First call—initialize Create(); }

} return pInstance_;

}…}

14/36

Проблема оброблена але не вирішена

Page 15: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ВИРІШЕННЯ – THE PHOENIX SINGLETON

void Singleton::OnDeadReference(){ // Отримати обгортку видаленого одинака

Create();//Тепер pInstance_ вказує на “попіл” singleton-у// - місце в пам’яті (raw memory) де знаходився singleton.//створюємо новий об’єкт на цьому місціnew(pInstance_) Singleton;// самі заносимо в стек ф-цію видалення нашого об’єктуatexit(KillPhoenixSingleton);// відновлюємо значення destroyed_ до falsedestroyed_ = false;

}void Singleton::KillPhoenixSingleton(){

//викликаємо деструктор вручну // він присвоїть pInstance_ нуль , а destroyed_ встановить truepInstance_->~Singleton();

}

15/36

Page 16: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ПРОБЛЕМИ Ф-ЦІЇ ATEXIT#include <cstdlib>void Bar(){...}void Foo(){std::atexit(Bar);}int main(){std::atexit(Foo);}

Рекомендація – добавити перевірку:#ifdef ATEXIT_FIXED// Queue this new object's destructoratexit(KillPhoenixSingleton);#endif

16/36

Page 17: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

SINGLETONS WITH LONGEVITY: ПЕРШІ КРОКИ

Забезпечити щоб Log мав довшу тривалість життя ніж Keyboard і Display

Хотілося б :// Singleton classclass SomeSingleton { ... };// another Singleton classclass SomeSingleton2 { ... };int main(){

SetLongevity(&SomeSingleton().Instance(), 5);// Гарантувати видалення SomeSingleton2 саме після першогоSetLongevity(&SomeSingleton2().Instance(), 6);

}

17/36

Page 18: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

SINGLETONS WITH LONGEVITY: DESIGN DECISIONS Кожен виклик SetLongevity ініціює виклик

atexit. Видалення об’єктів з меншою тривалістю

життя відбувається раніше ніж в об’єктів з більшою тривалістю.

Видалення об’єктів з однаковою тривалістю життя підпадає під стандартне правило мови С++ - останній створений перший зруйнований (LIFO).

18/36

Page 19: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

SINGLETONS WITH LONGEVITY: РЕАЛІЗАЦІЯ

namespace Private{

typedef LifetimeTracker** TrackerArray;extern TrackerArray pTrackerArray;extern unsigned int elements;

}//Helper destroyer functiontemplate <typename T>struct Deleter{

static void Delete(T* pObj) { delete pObj; }

}; 19/36

Page 20: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

SINGLETONS WITH LONGEVITY: РЕАЛІЗАЦІЯ

namespace Private{

class LifetimeTracker{public:LifetimeTracker(unsigned int x) : longevity_(x) {}virtual ~LifetimeTracker() = 0;friend inline bool Compare(unsigned int longevity,const LifetimeTracker* p) { return p->longevity_ > longevity; }private:unsigned int longevity_;

};// Definition requiredinline LifetimeTracker::~LifetimeTracker() {}}

20/36

Page 21: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

SINGLETONS WITH LONGEVITY: РЕАЛІЗАЦІЯ

class ConcreteLifetimeTracker : public LifetimeTracker{public:

ConcreteLifetimeTracker(T* p, unsigned int longevity, Destroyer d) : LifetimeTracker(longevity), pTracked_(p), destroyer_(d){}~ConcreteLifetimeTracker(){ destroyer_(pTracked_);}

private:T* pTracked_;Destroyer destroyer_;

};void AtExitFn();

21/36

Page 22: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

SINGLETONS WITH LONGEVITY: РЕАЛІЗАЦІЯ

template <typename T, typename Destroyer>void SetLongevity(T* pDynObject, unsigned int longevity,Destroyer d = Private::Deleter<T>::Delete){

TrackerArray pNewArray = static_cast<TrackerArray>(std::realloc(pTrackerArray, sizeof(T) * (elements + 1)));if (!pNewArray) throw std::bad_alloc();pTrackerArray = pNewArray;LifetimeTracker* p = new ConcreteLifetimeTracker<T, Destroyer>(pDynObject, longevity, d);TrackerArray pos = std::upper_bound(pTrackerArray, pTrackerArray + elements, longevity, Compare);std::copy_backward(pos, pTrackerArray + elements,pTrackerArray + elements + 1);*pos = p;++elements;std::atexit(AtExitFn);

}

22/36

Page 23: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

SINGLETONS WITH LONGEVITY: РЕАЛІЗАЦІЯ

static void AtExitFn(){

assert(elements > 0 && pTrackerArray != 0);// Pick the element at the top of the stackLifetimeTracker* pTop = pTrackerArray[elements - 1];// Remove that object off the stack// Don't check errors-realloc with less memory// can't failpTrackerArray = static_cast<TrackerArray>(std::realloc(pTrackerArray, sizeof(T) * --elements));// Destroy the elementdelete pTop;

}

23/36

Page 24: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

KDL PROBLEM: МАЙЖЕ ОСТАТОЧНЕ РІШЕННЯ

class Log{ public: static void Create() {

// Create the instancepInstance_ = new Log;// This line addedSetLongevity(*this, longevity_);

}// Rest of implementation omitted// Log::Instance remains as defined earlier private: // Define a fixed value for the longevity static const unsigned int longevity_ = 2; static Log* pInstance_;}; Keyboard та Display аналогічно, але longevity_ = 1

24/36

Page 25: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

БАГАТОПОТОКОВІСТЬ

Singleton& Singleton::Instance(){

if (!pInstance_) // 1 { pInstance_ = new Singleton; // 2 }return *pInstance_; // 3

}

25/36

Page 26: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

БАГАТОПОТОКОВІСТЬ: ПЕРШІ ІДЕЇ

Singleton& Singleton::Instance(){// mutex_ is a mutex object// Lock manages the mutexLock guard(mutex_);if (!pInstance_){

pInstance_ = new Singleton;}return *pInstance_;}

26/36

Page 27: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

БАГАТОПОТОКОВІСТЬ: ПОКРАЩЕННЯ

Singleton& Singleton::Instance(){ if (!pInstance_)

{Lock guard(mutex_);pInstance_ = new Singleton;}

return *pInstance_;}

27/36

Page 28: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

БАГАТОПОТОКОВІСТЬ: МАЙЖЕ НАЙКРАЩЕ.DOUBLE-CHECKED PATTERN

Singleton& Singleton::Instance(){ if (!pInstance_) // 1 { // 2 Guard myGuard(lock_); // 3 if (!pInstance_) // 4 {

pInstance_ = new Singleton; } }return *pInstance_;}

28/36

Хоча б: volatile pInstance_;

Page 29: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ОБ’ЄДНАННЯ ВСІХ ІДЕЙ Використання стратегій(див лекція 1). Розбиття:-стратегія Creation-стратегія Lifetime-стратегія Threading model

29/36

Page 30: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

РЕАЛІЗАЦІЯtemplate<class T,template <class> class CreationPolicy = CreateUsingNew,template <class> class LifetimePolicy = DefaultLifetime,template <class> class ThreadingModel = SingleThreaded>class SingletonHolder{public:static T& Instance();private:// Helpersstatic void DestroySingleton();// ProtectionSingletonHolder();...// Datatypedef ThreadingModel<T>::VolatileType InstanceType;static InstanceType* pInstance_;static bool destroyed_;};

30/36

Page 31: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ВИМОГИ ДО СТРАТЕГІЙ1)Сreation – створення і видалення об’єктуT* pObj = Creator<T>::Create();

Creator<T>::Destroy(pObj);

2)LifeTimeLifetime<T>::ScheduleDestruction(pDestructionFunctio

n);Lifetime<T>::OnDeadReference();3) Multithreadtemplate <class T> class SingleTh template <class T> class

MultiTh

{ {

... ...

public: public:

typedef T VolatileType; typedef volatile T VolatileType;

}; };

typedef ThreadingModel<T>::VolatileType InstanceType;

31/36

Page 32: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

32

МОЖЛИВІ СТРАТЕГІЇ

Page 33: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

НАВІТЬ ТАК

33/36

class A { ... };class Derived : public A { ... };template <class T> struct MyCreator : public

CreateUsingNew<T>{static T* Create(){

return new Derived;}};typedef SingletonHolder<A, StaticAllocator,

MyCreator> SingleA;

Page 34: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

ЗАДАЧА KDL – FINAL DECISION

34/36

class KeyboardImpl { ... };

class DisplayImpl { ... };

class LogImpl { ... };

inline unsigned int GetLongevity(KeyboardImpl*) { return 1; }

inline unsigned int GetLongevity(DisplayImpl*) { return 1; }

// The log has greater longevity

inline unsigned int GetLongevity(LogImpl*) { return 2; }

typedef SingletonHolder<KeyboardImpl, SingletonWithLongevity> Keyboard;

typedef SingletonHolder<DisplayImpl, SingletonWithLongevity> Display;

typedef SingletonHolder<LogImpl, SingletonWithLongevity> Log;

Page 35: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

NOTE

35/36

The implementation of Singleton put together here is not the do-it-all class. Only the features used are ultimately included in the generated code. Plus, the implementation leaves room for tweaks and extensions.

Page 36: T HE S INGLETON DESIGN PATTERN by A. Buniak. В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр,

36

Дякую за увагу