48
Singleton Duchenchuk Volodymyr Oksana Protsyk

Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Embed Size (px)

Citation preview

Page 1: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Singleton

Duchenchuk VolodymyrOksana Protsyk

Page 2: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

2 /48

Page 3: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

3 /48

Page 4: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

What use is that?

4 /48

Page 5: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

There are many objects we only need one of:- Caches- Dialog boxes- Objects that handle preferences and registry

settings- Objects used for logging

5 /48

Page 6: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Can’t I just do this by convention or by global variables?

6 /48

Page 7: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Singleton is a convention for ensuring one and only one object is instantiated for a given class.

Singleton Pattern gives global point of access, just like a global variable, but without the downsides.

7 /48

Page 8: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

What downsides?

8 /48

Page 9: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

An example: Global variable -> object is created when application begins.

“The road to programming hell is paved with global variables”

Steve McConnell

9 /48

Page 10: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public class Singleton {}

10 /48

Page 11: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public class Singleton {private Singleton() { }

}

11 /48

Page 12: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public class Singleton {private Singleton() { }

public static Singleton getInstance() {return new Singleton();

}}

12 /48

Page 13: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public class Singleton {private static Singleton uniqueInstance;

private Singleton() { }

public static Singleton getInstance() {if (uniqueInstance == null) {

uniqueInstance = new Singleton();}return uniqueInstance;

}

} 13 /48

Page 14: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public class Singleton {private static Singleton uniqueInstance;

private Singleton() { }// other useful instance variables herepublic static Singleton getInstance() {

if (uniqueInstance == null) {uniqueInstance = new Singleton();

}return uniqueInstance;

}//other useful methods here

} 14 /48

Page 15: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

The Chocolate Factory

All modern chocolate factories have computer controlled chocolate boilers.

The job of the boiler is to take in chocolate and milk, bring them to a boil, and then pass them on to the next phase of making chocolate bars.

15 /48

Page 16: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public class ChocolateBoiler {private boolean empty;private boolean boiled;

public boolean isEmpty() {return empty;

}

public boolean isBoiled() {return boiled;

}

//…}

16 /48

Page 17: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public ChocolateBoiler() {empty = true;boiled = false;

}

public void fill() {if (isEmpty()) {

empty = false;boiled = false;//fill the boiler with a milk/chocolate mixture

}}

17 /48

Page 18: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

public void boil() {if (!isEmpty() && !isBoiled()) {

boiled = true;//bring the contents to a boil

}}

public void drain() {if (!isEmpty() && isBoiled()) {

empty = true;//drain the boiled milk and chocolate

}}

18 /48

Page 19: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

How might things go wrong if more than one instance of ChocolateBoiler is created in an application?

19 /48

Page 20: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.

20 /48

Page 21: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

21 /48

Page 22: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Dealing with multithreadingpublic class Singleton {

private static Singleton uniqueInstance;private Singleton() { }// other useful instance variables herepublic static synchronized Singleton getInstance() {

if (uniqueInstance == null) {uniqueInstance = new Singleton();

}return uniqueInstance;

}//other useful methods here

} 22 /48

Page 23: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

23 /48

Page 24: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Options to improve the code1. Do nothing if the performance of getInstance() isn’t critical for your application

24 /48

Page 25: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Options to improve the code2. Move to an eagerly created instance rather than a lazily created one

public class Singleton {private static Singleton uniqueInstance = new Singleton();private Singleton() { }

public static Singleton getInstance() {return uniqueInstance;

}}

25 /48

Page 26: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Options to improve the code3. Use “double-checked locking” to reduce the use of synchronization in getInstance()

public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() { }public static Singleton getInstance() {

if (uniqueInstance == null) {synchronized (Singleton.class) {

if (uniqueInstance == null) {uniqueInstance = new Singleton();

}}

}return uniqueInstance;

}} 26 /48

Page 27: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Implementing Singleton (C++)

class MyClass {public:

static void doSomething();private:

static int _data;};

27 /48

Page 28: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

class Singleton{public:

static Singleton* Instance() {

if (!pInstance_)pInstance_ = new Singleton;

return pInstance_;}

private:Singleton(); Singleton(const Singleton&); static Singleton* pInstance_;

};

// Implementation file Singleton.cppSingleton* Singleton::pInstance_ = 0;

28 /48

Page 29: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

//improvedclass Singleton{public:

static Singleton& Instance();...

private:Singleton();Singleton(const Singleton&);Singleton& operator=(const Singleton&);~Singleton();...

};

29 /48

Page 30: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Destroying the Singleton

With the class definition we have destructor is never called.

30 /48

Page 31: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Meyers singleton

Singleton& Singleton::Instance(){

static Singleton obj;return obj;

}

31 /48

Page 32: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

A pseudo-C++ representation of the generated code by compiler

Singleton& Singleton::Instance(){

// Functions generated by the compilerextern void __ConstructSingleton(void* memory);extern void __DestroySingleton();// Variables generated by the compilerstatic bool __initialized = false;// Buffer that holds the singletonstatic char __buffer[sizeof(Singleton)];if (!__initialized){// First call, construct object// Will invoke Singleton::Singleton// In the __buffer memory__ConstructSingleton(__buffer);// register destructionatexit(__DestroySingleton);__initialized = true;}return *reinterpret_cast<Singleton*>(__buffer);

}

32 /48

Page 33: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

The Dead Reference Problem

33 /48

Three singletons: Keyboard, Display and Log.

Page 34: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

34 /48Keyboard

Display

Log

Page 35: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

35 /48

Should I use Meyers Singleton?

Page 36: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Keyboard is constructed successfully

36 /48

Example

Display fails to initialize

Display's constructor creates Log

Page 37: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Local static objects are destroyed in the reverse order of their creation.

Therefore, Log is destroyed before Keyboard.

Log::Instance now returns a reference to the "shell" of a destroyed Log object.

37 /48

Page 38: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Improvement

Let’s add static boolean member variable destroyed_

One function, one responsibility:Create, which effectively creates the SingletonOnDeadReference, which performs error handlingInstance, which gives access to the unique Singleton object.

38 /48

Page 39: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

static Singleton& Instance(){

if (!pInstance_){

// Check for dead referenceif (destroyed_){

OnDeadReference();}else{

// First call—initializeCreate();

}}return pInstance_;

}

39 /48

Page 40: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

// Create a new Singleton and store a// pointer to it in pInstance_static void Create();{

static Singleton theInstance;pInstance_ = &theInstance;

}

40 /48

// Gets called if dead reference detectedstatic void OnDeadReference(){

throw std::runtime_error("Dead Reference Detected");}

virtual ~Singleton(){

pInstance_ = 0;destroyed_ = true;

}

Page 41: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

41 /48

But that doesn’t really fix the problem!

Page 42: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

The Phoenix Singletonvoid Singleton::OnDeadReference(){

// Obtain the shell of the destroyed singletonCreate();// Now pInstance_ points to the "ashes" of the singleton// - the raw memory that the singleton was seated in.// Create a new singleton at that addressnew(pInstance_) Singleton;// Queue this new object's destructionatexit(KillPhoenixSingleton);// Reset destroyed_ because we're back in businessdestroyed_ = false;

}

void Singleton::KillPhoenixSingleton(){

pInstance_->~Singleton();}

42 /48

Page 43: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Singletons with Longevity// Takes a reference to an object allocated with new and// the longevity of that objecttemplate <typename T>void SetLongevity(T* pDynObject, unsigned int longevity);

• Each call to SetLongevity issues a call to atexit.• Destruction of objects with lesser longevity takes place before destruction of objects with greater longevity.• Destruction of objects with the same longevity follows the C++ rule: last built, first destroyed.

43 /48

Page 44: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

MultithreadingSingleton& Singleton::Instance(){

if (!pInstance_){

Lock guard(mutex_);if (!pInstance_) {

pInstance_ = new Singleton;}

}return *pInstance_;

}

44 /48

Page 45: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

In C++ 11 Meyers Singleton is thread-safety

Singleton& Singleton::Instance(){

static Singleton obj;return obj;

}

45 /48

Page 46: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Summary

• It's relatively easy to protect Singleton against multiple instantiations.

• The most complicated problem is managing a singleton's lifetime, especially its destruction (C++).

• There are threading issues surrounding the Singleton design pattern.

• There is no “best” solution.

46 /48

Page 47: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

References

• Head First Design Patterns by Eric Freeman, Elisabeth Robson, Bert Bates, Kathy Sierra

• Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma , Richard Helm, Ralph Johnson, John Vlissides

• Modern C++ Design: Generic Programming and Design Patterns Applied by Andrei Alexandrescu

47 /48

Page 48: Singleton Duchenchuk Volodymyr Oksana Protsyk. 2 /48

Thanks for attention!

48 /48