display | more...

The Singleton design pattern is an area of C++ philosophy where I happen to be in complete disagreement with the rest of the C++ world.  Singleton classes, as described in Gamma Et Al. are unnecessary; they add excess verbiage to code, and they are a symptom of bad design. I have described my reasons for saying this in my writeup called Don't Use Singleton Classes.

My challenge to you, the reader: describe for me a class that must be implemented using the form of the Singleton Pattern in Gamma Et Al.. Alternatively, describe one that is simpler, or works more efficiently, using that pattern. Put writeups to answer the challenge in this node and I will do my best to answer them. Let me know you did with a /msg.

You're going to have to work really hard to convince me that any singular object should have its behavior and singularity merged into a singleton class.

Suggested further reading: Arthur J. Riel, Object-Oriented Design Heuristics, 1996, Addison-Wesley, ISBN 0-201-63385-X.


I agree with Core's comments (Although I wish they'd been in the other node) for every OO language except C++. In C++ you can declare any global variable you want, exactly the way you do in C. You make things like singletons to enforce their working correctly. In C, you're working without a net.


I must challenge several points put forward by Rose Thorn:

  1. The number of classes in your program does not determine maintenance burden all by itself. Remember that small classes are easier to maintain. A singleton class that has two members for each function (one for the actual behavior and one to forward to the singular instance) is twice as complex as the server class or the client class separately. Regardless of whether we separate the client and server or merge them, we have the same amount of code to maintain!

  2. The client class is the real thing; it's the class the outside world uses. Suppose that not all of your class's behavior is singular. Where do you put the non-singular behavior? In the client class of course.

  3. Separation between the domain model and the implementation model is a Good Thing, not a Bad Thing. If by "re-engineering" you mean supplying a different implmentation, all you have to do is implement the server class interface to perform the behavior the client class expects. This is easier when you have separate classes. If you want to change the interface, you need to change both members under any model.

  4. Polymorphism is not the be-all and end-all of Object-Oriented Programming. Many C++ gurus will tell you that encapsulation is far more important. Encapsulation is about presenting an interface to the world, a contract between the class designer and the class user. If the contract can be satisfied without polymorphism, polymorphic member functions need not apply. Remember that Bjarne Stroustrup's original version of C with Classes, which made OOP something more than an academic toy, did not allow for polymorphism. Virtual functions were added later.

  5. I'd like to see a real model of a polymorphic class where every object must have its own behavior. For our theoretical Person class, we must be talking about an abstract base class Person from which a Joe class, a Jim class, and a MarySue class inherit. We want to pass Person objects into some other function somewhere, otherwise the Person class is meaningless. Joe has a magnetic personality, and along comes his disciple Bob, who acts exactly like Joe. All of a sudden, our assumption that there can be only one Joe is gone.

    (As an aside, It could be argued that a person's behavior is determined by our internal state, which comes from our initial genetic material [constructor arguments] plus outside environmental influences [mutator functions, input and output]. The internal processes that turn state into behavior (chemistry) remain the same.)

    For a different example, suppose that Ford and GM have quite different accounting practices. In an accounting application, we can model this with a FordAccountingSystem class and a GeneralMotorsAccountingSystem class. But, suppose Ford hired GM's CFO away form them. The new CFO replaces Ford's accounting system with an exact duplicate of GM's. We might be able to model Ford's new accounting system with a copy of General Motors's accounting system, except that we're that with the same AccountingSystem class as before. But if it's a singleton, we're stuck with separating the interface from the implementation again. The problem lies in having a GeneralMotorsAccountingSystem class rather than a SpiffyNewAccountingSystem class which can be applied to both Ford and GM.

The most tragic thing about singleton classes, much like many concepts in specialized fields, is just how non-special they are.

They're nothing more than a hack to get global variables in object oriented languages, and everyone knows it, but noone is willing to admit it.

So, we have a server class and a client class. We now have knowledge about our concept distributed over two separate classes, rather than one. This doubles our maintenance overhead. It also increases the separation between our domain model and our implementation model. Yay! Re-engineering just got harder: now your program explicitly has to reference it's own internal structure, rather than the knowledge of the domain. On it's own this is not a problem. In fact, it can make the program more maintainable in some (even many) cases, but it must be controlled. There is a danger that the client will be seen as "the real thing", and the server as just an item of convenience: suddenly logic starts popping up in the client code! Clients become more independant! Suddenly, the old oligarchic order falls as the plebian clients implement a republic!

As far as a time when a singleton is necessary, try having a program where you need an instance of an abstract class Person: you need every person to have their own behaviour, so they need their own class. If any two instances of person have the same class, then you have something seriously wrong with your program: a matter-transporter has probably malfunctioned. Of course, you needn't use a class when there is only one of a kind, as you won't need to allocate versions dynamically. But then you don't get polymorphic behaviour. While in this scenario you could use a variant of the visitor pattern to do the kind of things you might do with mixins, you lose the ability to very strongly enforce your uniqueness constraints.

If you don't want polymorphism, don't use an OO implementation. Polymorphism is what give OO it's encapsulation, because method calls are referentially transparent. The use of classes as namespaces is something that is pretty much idiosyncratic to C++ derived languages. Certainly Objective C and Common Lisp don't do it. Namespace division is done by a separate mechanism. The use of private members is not the same as information hiding, and inheritance and polymorphism are not the same thing.

You CAN implement singleton classes nicely: every public method delegates to a private method which is invoked on the single "real" member of the class. Java example follows:

class Foo
{
    private static Foo theInstance;

    public Foo()
    {
         if(theInstance==null)
         {
             theInstance = this;
         }
    }

     public void frob(int i)
     {
        theInstance.priv_frob(i);
     }

     private void priv_frob(int i)
     {
         //do whatever
     }

}

Log in or register to write something here or to contact authors.