Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


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

1 T HE S INGLETON DESIGN PATTERN by A. Buniak

2 В СТУП + ПОВТОРЕННЯ Шаблон Singleton гарантує, що певний клас матиме лише один екземпляр, і забезпечує глобальний доступ до нього Не дивлячись на легку постановку задачі, з’являються значні труднощі при реалізації 2/36

3 Щ О МИ ВИВЧИМО Риси, що відрізняють Singleton від звичайної глобальної змінної Основні ідіоми мови С++ для підтримки сінглтонів Забезпечення унікальності сінглтону Видалення сінглтона, керування тривалістю життя сінглтона, звернення до нього після видалення. Багатопотоковість 3/36

4 П ОСТАНОВКА ЗАДАЧІ Три класи-одинака : Keyboard, Display, Log Log генерує повідомлення про помилки (якщо їх немає, Log взагалі не створюється). Класу Log надсилаються всі помилки, які виникають при створенні та видаленні класів Keyboard та Display. 4/36

5 П ЕРШІ ІДЕЇ Лише статичні функції і статичні дані сlass Singleton { public: static void DoJob1(); … private: static int pole1; … } 5/36

6 S TATIC D ATA + S TATIC F UNCTIONS != S INGLETON Лише статичні функції і статичні дані сlass Singleton { public: static void DoJob1(); … private: static int pole1; … } 1. Статичні функції не можуть бути віртуальними 2. Труднощі з ініціалізацією і видаленням об’єктів. 6/36

7 Б ІЛЬШ ЗВИЧНЕ РІШЕННЯ ( ПОВТОРЕННЯ ) // 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

8 В АЖЛИВА ДЕТАЛЬ // 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();

9 В ИДАЛЕННЯ S INGLETON Memory leak має місце якщо конструктор одинака запросить необмежену к-сть ресурсів Єдиний вихід – видаляти об’єкт класу Singleton при виході з програми The Meyers Singleton Singleton& Singleton::Instance() { static Singleton obj; return obj; } 9/36

10 П СЕВДОКОД 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 (__buffer); } 10/36

11 T HE D EAD R EFERENCE P ROBLEM Контр приклад: 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

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

13 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

14 class Singleton { public: Singleton& Instance() { if (!pInstance_) { // Check for dead reference if (destroyed_) { OnDeadReference(); } else { // First call—initialize Create(); } return pInstance_; } … } 14/36 Проблема оброблена але не вирішена

15 В ИРІШЕННЯ – T HE P HOENIX S INGLETON void Singleton::OnDeadReference() { // Отримати обгортку видаленого одинака Create(); //Тепер pInstance_ вказує на “попіл” singleton-у // - місце в пам’яті (raw memory) де знаходився singleton. //створюємо новий об’єкт на цьому місці new(pInstance_) Singleton; // самі заносимо в стек ф-цію видалення нашого об’єкту atexit(KillPhoenixSingleton); // відновлюємо значення destroyed_ до false destroyed_ = false; } void Singleton::KillPhoenixSingleton() { //викликаємо деструктор вручну // він присвоїть pInstance_ нуль, а destroyed_ встановить true pInstance_->~Singleton(); } 15/36

16 П РОБЛЕМИ Ф - ЦІЇ ATEXIT #include void Bar() {... } void Foo() { std::atexit(Bar); } int main() { std::atexit(Foo); } Рекомендація – добавити перевірку: #ifdef ATEXIT_FIXED // Queue this new object's destructor atexit(KillPhoenixSingleton); #endif 16/36

17 S INGLETONS WITH L ONGEVITY : ПЕРШІ КРОКИ Забезпечити щоб Log мав довшу тривалість життя ніж Keyboard і Display Хотілося б : // Singleton class class SomeSingleton {... }; // another Singleton class class SomeSingleton2 {... }; int main() { SetLongevity(&SomeSingleton().Instance(), 5); // Гарантувати видалення SomeSingleton2 саме після першого SetLongevity(&SomeSingleton2().Instance(), 6); } 17/36

18 S INGLETONS WITH L ONGEVITY : DESIGN DECISIONS Кожен виклик SetLongevity ініціює виклик atexit. Видалення об’єктів з меншою тривалістю життя відбувається раніше ніж в об’єктів з більшою тривалістю. Видалення об’єктів з однаковою тривалістю життя підпадає під стандартне правило мови С++ - останній створений перший зруйнований (LIFO). 18/36

19 S INGLETONS WITH L ONGEVITY : Р ЕАЛІЗАЦІЯ namespace Private { typedef LifetimeTracker** TrackerArray; extern TrackerArray pTrackerArray; extern unsigned int elements; } //Helper destroyer function template struct Deleter { static void Delete(T* pObj) { delete pObj; } }; 19/36

20 S INGLETONS WITH L ONGEVITY : Р ЕАЛІЗАЦІЯ 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 required inline LifetimeTracker::~LifetimeTracker() {} } 20/36

21 S INGLETONS WITH L ONGEVITY : Р ЕАЛІЗАЦІЯ 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

22 S INGLETONS WITH L ONGEVITY : Р ЕАЛІЗАЦІЯ template void SetLongevity(T* pDynObject, unsigned int longevity, Destroyer d = Private::Deleter ::Delete) { TrackerArray pNewArray = static_cast ( std::realloc(pTrackerArray, sizeof(T) * (elements + 1))); if (!pNewArray) throw std::bad_alloc(); pTrackerArray = pNewArray; LifetimeTracker* p = new ConcreteLifetimeTracker ( 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

23 S INGLETONS WITH L ONGEVITY : Р ЕАЛІЗАЦІЯ static void AtExitFn() { assert(elements > 0 && pTrackerArray != 0); // Pick the element at the top of the stack LifetimeTracker* pTop = pTrackerArray[elements - 1]; // Remove that object off the stack // Don't check errors-realloc with less memory // can't fail pTrackerArray = static_cast (std::realloc( pTrackerArray, sizeof(T) * --elements)); // Destroy the element delete pTop; } 23/36

24 KDL PROBLEM : МАЙЖЕ ОСТАТОЧНЕ РІШЕННЯ class Log { public: static void Create() { // Create the instance pInstance_ = new Log; // This line added SetLongevity(*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

25 Б АГАТОПОТОКОВІСТЬ Singleton& Singleton::Instance() { if (!pInstance_) // 1 { pInstance_ = new Singleton; // 2 } return *pInstance_; // 3 } 25/36

26 Б АГАТОПОТОКОВІСТЬ : ПЕРШІ ІДЕЇ Singleton& Singleton::Instance() { // mutex_ is a mutex object // Lock manages the mutex Lock guard(mutex_); if (!pInstance_) { pInstance_ = new Singleton; } return *pInstance_; } 26/36

27 Б АГАТОПОТОКОВІСТЬ : ПОКРАЩЕННЯ Singleton& Singleton::Instance() { if (!pInstance_) { Lock guard(mutex_); pInstance_ = new Singleton; } return *pInstance_; } 27/36

28 Б АГАТОПОТОКОВІСТЬ : МАЙЖЕ НАЙКРАЩЕ. D OUBLE -C HECKED PATTERN Singleton& Singleton::Instance() { if (!pInstance_) // 1 { // 2 Guard myGuard(lock_); // 3 if (!pInstance_) // 4 { pInstance_ = new Singleton; } return *pInstance_; } 28/36 Хоча б: volatile pInstance_;

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

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

31 В ИМОГИ ДО СТРАТЕГІЙ 1)Сreation – створення і видалення об’єкту T* pObj = Creator ::Create(); Creator ::Destroy(pObj); 2)LifeTime Lifetime ::ScheduleDestruction(pDestructionFunction); Lifetime ::OnDeadReference(); 3) Multithread template class SingleTh template class MultiTh {... public: typedef T VolatileType; typedef volatile T VolatileType; }; typedef ThreadingModel ::VolatileType InstanceType; 31/36

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

33 Н АВІТЬ ТАК 33/36 class A {... }; class Derived : public A {... }; template struct MyCreator : public CreateUsingNew { static T* Create() { return new Derived; } }; typedef SingletonHolder SingleA;

34 З АДАЧА 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 Keyboard; typedef SingletonHolder Display; typedef SingletonHolder Log;

35 N OTE 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.

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


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

Similar presentations


Ads by Google