转载:Singleton Pattern
xlsue 2006-04-21 08:07:30 Singleton Pattern
Last updated Feb 13, 2006.
Singleton is perhaps the most familiar Design Pattern. Its intent is to ensure that a class only has one instance, and provide a global point of access to it. In this regard, Singleton isn't much different from a monostate class, although there are many situations in which a true Singleton class is required. Today, I will present the guidelines of writing such a class and demonstrate its usefulness.
Rationale
In many scenarios, you need exactly one object in a system, application, or device. Consider a printer spooler, for example. Although there can be many printers in a system, there should be only one printer spooler. Similarly, your operating system uses only one file system, and if it's connected to the Internet, it usually has only one active Internet connection at a time.
Ensuring uniqueness without the use of a Singleton class is difficult and error-prone. The Singleton pattern provides a skeletal template for such a class, which you may extend and modify according to your specific needs.
Implementation
Singleton ensures that only a single instance of its type can be created by intercepting requests to create new objects. In addition, it provides a means of accessing the sole instance (also known as the Singleton object). To ensure that clients cannot create objects directly, Singleton has a protected constructor. In addition, it contains a static member function called Instance(), which returns a pointer to its sole instance:
class Singleton
{
public:
static Singleton* Instance();
protected:
Singleton();
private:
static Singleton* _instance;
};
The private static data member _instance is a pointer to the sole instance. Singleton uses lazy initialization, meaning that the value returned from Instance() isn't created until this member function is first called:
Singleton* Singleton::_instance = 0;
Singleton* Singleton::Instance ()
{
if (_instance == 0)
instance = new Singleton;
return _instance;
}
Design Considerations
The constructor is responsible for all the initialization operations of the Singleton object. For example, if we're creating a Singleton print spooler, it will create an empty job queue, check the status of all the currently active printers, etc. Because the constructor is protected, a client that tries to instantiate Singleton object directly will get a compilation error. Note that this constructor will get called on the first call to Instance().
Storing a pointer rather than an actual object has two advantages. First, it enables Singleton to use lazy instantiation. Secondly, it supports polymorphism -- you may assign the pointer to a subclass of Singleton.
The Design Patterns literature elegantly ignores the issue of a Singleton's destructor. This isn't a trivial issue, as it may seem at first. Remember that if you intend to derive classes from Singleton, you must declare its destructor virtual. Alternatively, you may forgo destructors both in the base class and its descendants. However, if you do define a destructor, don't be tempted to use it to destroy the Singleton object. The problem is that calling:
delete _instance;will invoke the destructor, which in turn will try to delete _instance once more, infinitely. That said, don't ignore the fact that Instance() allocates an object on the free-store, and that that object must be deleted explicitly to avoid a memory leak. The simplest solution is to delete _instance at the end of the program:
int main()
{
Singleton *p = Singleton::Instance();
//..use p
delete p;
}
This is however, a violation of basic OOD principles. Imagine what would happen if another thread calls Instance() one again. A better solution is to define another static member function that explicitly destroys the Singleton object and resets _instance:
void Singleton::Destroy()
{
delete _instance;
_instance=0;
}
This way, a subsequent call to Instance() will work as expected, because it checks whether _instance is 0 before returning. Another solution that is suitable for single-threaded environments is to use a local static object:
//using a static Singleton object
//not suitable for multithreaded apps
Singleton* Singleton::Instance ()
{
static Singleton s;
return &s; // _instance isn't needed in this case
}
Applications
Suppose you have a network card through which different applications access a local network. To ensure uniqueness, you choose to implement the network card as a Singleton class. Because several types of network cards are used on different machines, you decided to derive different classes from Singleton, each of which representing a different brand.
Instance() determines at runtime the actual network card type by examining an environment variable. It then constructs an object of the right type and returns its address to the caller:
class NetCard // a Singleton class
{
public:
static NetCard * Instance();
protected:
NetCard ();
private:
static NetCard * _instance;
};
NetCard * NetCard::_instance=0;
NetCard* NetCard::Instance()
{
if (_instance == 0)
{
const string* type = getenv("CARDTYPE");
if (type == "Rockwell")
_instance = new RockwellNetCard;
else if (type == "Samsung")
_instance = new SamsungNetCard;
// ... other types
else
_instance = new NetCard; //default card
}
return _instance;
}
Summary
Singleton is not a panacea. The implementation of NetCard::Instance() already pushes the design to its limits; it provides some level of dynamic typing, but if you add more derived classes, you'll need to modify Instance() accordingly. If you need a mechanism for dynamic type creation, consider using the Abstract Factory pattern instead.
Books
Design Patterns: Elements of Reusable Object-Oriented Software (by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) has been the ultimate pattern catalog ever since it was published in 1994. Although many patterns have been developed after its publication (monostate, for instance), it's still one of the most influential programming books ever written that every programmer should read.