473,320 Members | 1,848 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes and contribute your articles to a community of 473,320 developers and data experts.

Design Patterns: The Singleton

weaknessforcats
9,208 Expert Mod 8TB
Design Pattern: The Singleton

Overview

Use the Singleton Design Pattern when you want to have only one instance of a class. This single instance must have a single global point of access. That is, regardless of where the object is hidden, everyone needs access to it.

The global point of access is the object's Instance() method.

Individual users need to be prevented from creating their own instances of the Singleton.

The Singleton represents a replacement for a global variable. Professional C++ developers avoid global variables for a variety of reasons.

Where many Singleton objects are required by an application, it may be necessary to construct a container of Singletons called a registry. Here individual Singleton objects can be stored with a key for retrieval.

A Singleton Class – Part 1

The SysParms class shown represents a class capable of being a Singleton. The default constructor is protected so only a derived class can execute it. This makes it possible to create an instance of the derived class but not an instance of the SysParms class itself.

Likewise the SysParms destructor is protected so it can be called only by a derived object. This will prevent just any user from deleting the Singleton. Of course, the destructor is virtual which allows the SysParms class to be used with polymorphism.

The Instance() method is static so you do not need a SysParms object to call it. In fact, it will be the Instance() method that you will use to create the SysParms object in the first place. This is called the lazy method of creating the object.

Expand|Select|Wrap|Line Numbers
  1.  
  2. class SysParms
  3. {     
  4.    private:
  5.         SysParms(const SysParms&);  //Prevents making a copy
  6.  
  7.    protected:
  8.         SysParms();       //Only a SysParms member can call this
  9.                      //Prevents a user from creating singleton objects
  10.         virtual ~SysParms();  //Prevents just anyone from deleting the singleton
  11.  
  12.    public:
  13.  
  14.  
  15.        //The "official" access point.
  16.        static SysParms* Instance();
  17.  
  18. };
  19.  
  20.  
The implementation of the SysParms class would be in a source file by itself.

Here you see the address of the single instance implemented as a variable inside an anonymous namespace. The reason for this is that the members of an anonymous namespace are in accessible outside the file where the namespace resides. That is, they have internal linkage.

The initial value of the address of the instance is set to 0. The SysParms singleton will not actually by created until it is required.

The static SysParms::Instance() method simply checks the address of the instance in the anonymous namespace and if the address is zero, the method creates a new SysParms object on the heap and stores the heap address in the namespace.

If the address in the anonymous namespace is not zero, it means the singleton has already been created. In this case, the Instance() methods just returns the address in the namespace.

Expand|Select|Wrap|Line Numbers
  1.  
  2. //Use anonymous namespace to force internal linkage for instance
  3.  
  4. namespace
  5. {
  6.     SysParms* instance = 0;        //Address of the singleton
  7.  
  8. }
  9.  
  10. SysParms::SysParms()
  11. {
  12.  
  13. }
  14. SysParms::~SysParms()
  15. {
  16.     delete instance;
  17.                 instance = 0;
  18. }
  19.  
  20. //The "official" access point
  21. SysParms* SysParms::Instance()
  22. {
  23.    //"Lazy" initialization. Singleton not created until it's needed
  24.    if (!instance)
  25.    {
  26.       instance = new SysParms;
  27.    }
  28.    return instance;
  29. }
  30.  
Using a Simple Singleton

To use the SysParms singleton, you need only call the Instance() method and you will receive the address of the singleton.

From there, you may execute whatever methods are on the singleton.

Expand|Select|Wrap|Line Numbers
  1. int main()
  2. {
  3.    SysParms* theSysParmsSingleton;
  4.    cout << "Example A: Creating only one instance" << endl;
  5.  
  6.    //
  7.    //SysParms obj;  //ERROR: singleton objects cannot be created by the user
  8.    //
  9.    //Use Instance() method to locate singleton. 
  10.    //Instance() is static as we have no object
  11.    theSysParmsSingleton = SysParms::Instance();
  12.    //
  13.    cout << "theSysParmsSingleton is located at " << &theSysParmsSingleton 
  14.           << endl;
  15.  
  16.  
  17.  
  18.  
  19.    return 0;
  20. }
  21.  
A Singleton Class – Part 2

In this second part, the SysParms class is expanded and a derived class is added. Instances of the derived class, which are all singletons, are stored in a container, called a registry, with a retrieval key.

In this example, the singleton objects deal with sort sequences so there will be a singleton object for each of the sort sequences.

In this case, the sort sequences are either ascending or descending.

A Register() method has been added to store the singleton in the registry.

A Lookup() method has been added to retrieve singletons from the registry.

Lastly, there are accessor methods for changing or retrieving parameters from the singleton. For ease, the parameters are just a simple enum. In a real program the parameters would be returned as an object of a Parameter class.

Please note: Public virtual functions are discouraged because public virtual functions violate the concept of separation of interface from implementation. They are shown as public in this example for instructional purposes only.

Expand|Select|Wrap|Line Numbers
  1. class SysParms
  2.     private:
  3.          SysParms(const SysParms&);  //Prevents making a copy
  4.  
  5.     protected:
  6.          SysParms();       //Only a SysParms member or subclass can call this
  7.          virtual ~SysParms(); //Prevent just anyone from deleting the singleton
  8.  
  9.     public:
  10.        enum Parms {ASCENDING, DESCENDING};
  11.  
  12.  
  13.  
  14.     //The "official" access point. 
  15.     static SysParms* Instance(const std::string& name);
  16.  
  17.     //Register the singleton object by a name
  18.     //Returns false if registration failed
  19.     bool Register(const std::string& name, SysParms* obj);
  20.  
  21.     //Returns 0 if name not in registry
  22.     SysParms* Lookup(const std::string& name);
  23.  
  24.    //Parameter Interface:
  25.    //These should really be private. 
  26.    virtual Parms GetParms()= 0; 
  27.    virtual void SetParms(Parms in) = 0;
  28.  
  29. }; 
  30.  
The SysParms implementation file now expands to:

Expand|Select|Wrap|Line Numbers
  1. extern SingletonRegistry theRegistry;
  2.  
  3.  
  4. SysParms::SysParms()
  5. {
  6.  
  7. }
  8. SysParms::~SysParms()
  9. {
  10.  
  11. }
  12.  
  13. //The "official" access point
  14. //
  15. //You look up the singleton in theRegistry and return its address
  16. //
  17. SysParms* SysParms::Instance(const string& name)
  18. {
  19.  
  20.     return theRegistry.Lookup(name);
  21.  
  22. }
  23.  
  24. bool SysParms::Register(const string& name, SysParms* obj)
  25. {
  26.     return theRegistry.Add(name, obj);
  27. }
  28.  
  29. SysParms* SysParms::Lookup(const string& name)
  30. {
  31.     return theRegistry.Lookup(name);
  32. }
  33.  
You can see the implementation of the Register() and Lookup() methods. The registry itself is in its own implementation file.

Here an STL map is used for the registry.

Expand|Select|Wrap|Line Numbers
  1. class SingletonRegistry
  2. {
  3.    private:
  4.     std::map<std::string, SysParms*> theRegistry;        
  5.  
  6.    public:
  7.     //Default ctor and dtor of std::map<> will be used
  8.  
  9.     //Locate 
  10.     SysParms*   Lookup(const std::string& name);        
  11.  
  12.     //Add
  13.     bool Add(const std::string& name, SysParms* obj);
  14. };
  15.  
The registry implementation is shown below.

A singleton is stored in the registry simply by creating a pair<> object with the supplied string as the retrieval key and a pointer to the singleton object. The pair is inserted in the map container.

A singleton is retrieved by accessing the map container using the string specified to retrieve the pair object from the map container. The second member of the pair is the address of the singleton corresponding to the supplied string. The method returns a null pointer if the singleton is not in the registry.

Expand|Select|Wrap|Line Numbers
  1. extern SingletonRegistry theRegistry;
  2.  
  3. //Registry of Singletons.
  4.  
  5. SysParms* SingletonRegistry::Lookup(const string& name)
  6. {
  7.     pair<string, SysParms*> theEntry;
  8.     map<string, SysParms*>::iterator itr = theRegistry.find(name);
  9.     if (itr == theRegistry.end())
  10.     {
  11.         return 0;  //not found
  12.     }
  13.     return itr->second;    
  14. }
  15.  
  16. bool SingletonRegistry::Add(const string& name, SysParms* obj)
  17. {
  18.     pair<string, SysParms*> theEntry;
  19.     theEntry.first = name;
  20.     theEntry.second = obj;
  21.  
  22.     theRegistry.insert(theEntry);
  23.  
  24.     return true;  
  25.  
  26. }
  27.  
The singletons being put in the registry are:
Expand|Select|Wrap|Line Numbers
  1. class SortParms :public SysParms
  2. {
  3.  
  4.    private:
  5.      enum  Parms seq;
  6.  
  7.  
  8.    public:
  9.     SortParms(const std::string& name);
  10.     ~SortParms();
  11.     virtual Parms GetParms(); 
  12.     virtual void SetParms(Parms in);
  13. };
  14.  
And are implemented as:

Expand|Select|Wrap|Line Numbers
  1. extern SingletonRegistry theRegistry;
  2.  
  3. SortParms::SortParms(const std::string& name)
  4. {
  5.     theRegistry.Add(name, this);
  6. }
  7. SortParms::~SortParms()
  8. {
  9.  
  10. }
  11.  
  12.  
  13.  
  14. enum SysParms::Parms SortParms::GetParms()
  15. {
  16.     return this->seq;
  17.  
  18. }
  19. void SortParms::SetParms(Parms in)
  20. {
  21.         this->seq = in;
  22. }
  23.  
Notice these singletons register themselves when they are created by having the register code as part of the singleton constructor.

In a separate source file, the registry is created followed by the singletons. Again, the singleton objects are in an anonymous namespace which makes them inaccessible from anywhere. The only global point of reference not is the Lookup() method on the registry. Here is the implementation:

Expand|Select|Wrap|Line Numbers
  1. //The Registry object
  2.  
  3.     SingletonRegistry theRegistry;
  4. //
  5. //The SortParms objects
  6. //
  7. //The anonymous namespace makes them publicly inaccessible outside this file.
  8. //
  9. namespace
  10. {
  11.    SortParms obj(std::string("SortParms"));       //The singleton itself
  12.    SortParms obj1(std::string("SortParmsForPerson"));    //The singleton itself
  13.    SortParms obj2(std::string("SortParmsForDate"));    //The singleton itself
  14. }
  15.  
The above implementation is based on the feature that global variables are created in the order in which they are declared and that all global variables must be created before main() starts.

Based on that, you can see the registry is created followed by three singletons. These singletons register themselves using the string in the constructors.

Users must now call the Instance() method with one of those strings. The Instance() will use the string to look up the singleton in the registry and return a pointer to it.

Expand|Select|Wrap|Line Numbers
  1.  
  2. int main()
  3. {
  4.    SysParms* theSysParmsSingleton;
  5.    cout << "Example B: Creating a registered singleton" << endl;
  6.  
  7.    //
  8.    //SysParms obj;  //ERROR: singleton objects cannot be created by the user
  9.    //
  10.    //Use Instance() method to locate singleton. 
  11.    theSysParmsSingleton = SysParms::Instance("SortParms");
  12.  
  13.    //
  14.    cout << "the SortParms Singleton is located at " << theSysParmsSingleton 
  15.           << endl;
  16.  
  17.    theSysParmsSingleton = SysParms::Instance("SortParmsForPerson");
  18.  
  19.    //
  20.    cout << "the SortParmsForPerson Singleton is located at "
  21.            <<   theSysParmsSingleton << endl;
  22.  
  23.    theSysParmsSingleton = SysParms::Instance("SortParmsForDate");
  24.  
  25.    //
  26.    cout << "the SortParmsForDate Singleton is located at "
  27.           << theSysParmsSingleton << endl;
  28.  
  29.    //Set a sort sequence for Person
  30.    SysParms::Instance("SortParmsForPerson")->SetParms(SysParms::ASCENDING); 
  31.    //Set a sort sequence for Date
  32.    SysParms::Instance("SortParmsForDate")->SetParms(SysParms::DESCENDING); 
  33.  
  34.    //Retrieve sort sequence
  35.    enum SysParms::Parms rval = SysParms::Instance("SortParmsForPerson")->GetParms();
  36.    if (rval == SysParms::ASCENDING)
  37.    { 
  38.       cout << "Sort Person Ascending" << endl;
  39.    }
  40.    else
  41.    {
  42.       cout << "Sort Person Descending" << endl;
  43.    }
  44.  
  45.    //Retrieve sort sequence
  46.    rval = SysParms::Instance("SortParmsForDate")->GetParms();
  47.    if (rval == SysParms::ASCENDING)
  48.    {
  49.       cout << "Sort Date Ascending" << endl;
  50.    }
  51.    else
  52.    {
  53.       cout << "Sort Date Descending" << endl;
  54.    }
  55.    return 0;
  56. }
  57.  
Using Handles
The examples in this article use pointers since pointer syntax is commonly understood. However, it is recommended that, in a real application, handles be used. You should refer to the article on Handles in the C/C++ Articles section.

Further Information
Refer to the book Design Patterns by Erich Fromm, et al, Addison-Wesley 1994.

This article shows only the conceptual basis of the Singleton pattern but not motivations and ramifications of using this pattern.

Copyright 2007 Buchmiller Technical Associates North Bend WA USA
Jun 1 '07 #1
3 18185
kovik
1,044 Expert 1GB
The Singleton represents a replacement for a global variable. Professional C++ developers avoid global variables for a variety of reasons.
I'd just like to add that the Singleton pattern is almost as dangerous as global variables, for the same reasons, as long as you are retrieving the same object by reference.
Jul 1 '07 #2
pootle
68
You should also note that the singleton pattern as presented here is not thread safe. This is an issue when a pre-emptive kernel is involved. To make a singleton thread safe, the creation of the instance needs to be protected.
Apr 30 '08 #3
weaknessforcats
9,208 Expert Mod 8TB
You should also note that the singleton pattern as presented here is not thread safe.
That is correct. Thread safety is an issue only when multiple threads are involved and not all programs are multithreaded.

I felt is necessarry to stick with the pattern only.
May 5 '08 #4

Sign in to post your reply or Sign up for a free account.

Similar topics

4
by: Neil Zanella | last post by:
Hello, I would be very interested in knowing how the following C++ multi-instance singleton (AKA Borg) design pattern based code snippet can be neatly coded in Python. While there may be...
4
by: Tony Ha | last post by:
Hello I am learning Python for in the pass ten months, and have brought a few books about Python. Most of them are good books by its only right, and all of them only teach you how to write...
4
by: Joakim Olesen | last post by:
Hi, I'm looking for a book/books about design patterns (factory patterns, singleton patterns etc). The book doesn't have to be aimed towards C# developers in particular, but the patterns should...
4
by: Frazer | last post by:
hi are there any good samples illustrating design patterns in C#? builder, adapter, facade etc. i found a few but the reviews of that article were bad. eg...
13
by: John Salerno | last post by:
Here are a few I'm considering: Design Patterns Explained : A New Perspective on Object-Oriented Design (2nd Edition) (Software Patterns Series) by Alan Shalloway Design Patterns C# by...
24
by: John Salerno | last post by:
Since Python does so many things different, especially compared to compiled and statically typed languages, do most of the basic design patterns still apply when writing Python code? If I were to...
22
by: Krivenok Dmitry | last post by:
Hello All! I am trying to implement my own Design Patterns Library. I have read the following documentation about Observer Pattern: 1) Design Patterns by GoF Classic description of Observer....
6
by: abcd | last post by:
I have 13 yrs experience working from Visual Studio 1.x today VS 2005 (both VB and C++) Most of the time I have worked in N-tier applications, Web applications, Windows applications....My...
10
by: vital | last post by:
Hi, I am designing the middle tier of a project. It has 6 classes and microsoft application data access block. The six classes are DBServices, Logger, ProjectServices ... etc. and all these...
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
0
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.