Singleton
Duchenchuk VolodymyrOksana Protsyk
2 /48
3 /48
What use is that?
4 /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
Can’t I just do this by convention or by global variables?
6 /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
What downsides?
8 /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
public class Singleton {}
10 /48
public class Singleton {private Singleton() { }
}
11 /48
public class Singleton {private Singleton() { }
public static Singleton getInstance() {return new Singleton();
}}
12 /48
public class Singleton {private static Singleton uniqueInstance;
private Singleton() { }
public static Singleton getInstance() {if (uniqueInstance == null) {
uniqueInstance = new Singleton();}return uniqueInstance;
}
} 13 /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
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
public class ChocolateBoiler {private boolean empty;private boolean boiled;
public boolean isEmpty() {return empty;
}
public boolean isBoiled() {return boiled;
}
//…}
16 /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
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
How might things go wrong if more than one instance of ChocolateBoiler is created in an application?
19 /48
The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.
20 /48
21 /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
23 /48
Options to improve the code1. Do nothing if the performance of getInstance() isn’t critical for your application
24 /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
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
Implementing Singleton (C++)
class MyClass {public:
static void doSomething();private:
static int _data;};
27 /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
//improvedclass Singleton{public:
static Singleton& Instance();...
private:Singleton();Singleton(const Singleton&);Singleton& operator=(const Singleton&);~Singleton();...
};
29 /48
Destroying the Singleton
With the class definition we have destructor is never called.
30 /48
Meyers singleton
Singleton& Singleton::Instance(){
static Singleton obj;return obj;
}
31 /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
The Dead Reference Problem
33 /48
Three singletons: Keyboard, Display and Log.
34 /48Keyboard
Display
Log
35 /48
Should I use Meyers Singleton?
Keyboard is constructed successfully
36 /48
Example
Display fails to initialize
Display's constructor creates Log
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
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
static Singleton& Instance(){
if (!pInstance_){
// Check for dead referenceif (destroyed_){
OnDeadReference();}else{
// First call—initializeCreate();
}}return pInstance_;
}
39 /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;
}
41 /48
But that doesn’t really fix the problem!
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
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
MultithreadingSingleton& Singleton::Instance(){
if (!pInstance_){
Lock guard(mutex_);if (!pInstance_) {
pInstance_ = new Singleton;}
}return *pInstance_;
}
44 /48
In C++ 11 Meyers Singleton is thread-safety
Singleton& Singleton::Instance(){
static Singleton obj;return obj;
}
45 /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
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
Thanks for attention!
48 /48