- Global objects are bad
- Singleton Design Pattern
- Static initialization order fiasco
- How to hide a singleton
- But only one of these should ever exist
- Wait a moment…
- Summary
But only one of these should ever exist
Throughout the teaching of C++ there have been some popular examples to describe object orientation. Gas stations have cars, pumps, a cash desk, tankers delivering fuel, prices, and so on, yielding an ecosystem rich enough to describe many kinds of relationships. In the same vein, restaurants have tables, customers, menus, a serving hatch, wait staff, chefs, food deliveries, garbage collection, and other features. In today’s textbooks they probably also have a website and a Twitter account.
Both examples have one thing in common: an abstraction that should only exist singly. The gas station has one cash desk. The restaurant has one serving hatch. Surely these are singletons? If not, what is to be done?
One solution we have seen to this problem is to create a class with an entirely static interface. All the public member functions and the private data are static. We now want to take a diversion and tell you about W. Heath Robinson. Born in 1872 in Finsbury Park, London, this English cartoonist was best known for his drawings of ludicrously elaborate machines that went to great lengths to solve simple problems. One of the automatic analysis machines built for Bletchley Park during the Second World War to assist in the decryption of German message traffic was named “Heath Robinson” in his honor. I was given a book of his cartoons as a young child and marveled at the intricacy of the operation of his devices. He had an American coun-terpart, Rube Goldberg, born in July 1883 in San Francisco, who also drew overly complex devices, and inspired the board game Mouse Trap. Their names have passed into common parlance in the English language to describe overengineering.
This is precisely what a class with an entirely static interface is an example of. When you create a class, you create a public interface for viewing and controlling the abstraction, and a pile of data and nonpublic functions for modeling the abstraction. However, if there is only one instance of all the data, why do you need to attach it to a class? You can simply implement all the public member functions in one source file and put the single instance of the data and all the nonpublic functions in an anony-mous namespace.
In fact, why are you bothering with a class at all?
What we have arrived at, in a self-referentially convoluted way, is the correct solution to the problem of singletons (small s). They should be implemented as namespaces rather than classes. Rather than this:
class Manager { public: static int blimp_count(); static void add_more_blimps(int); static void destroy_blimp(int); private: static std::vector<Blimp> blimps; static void deploy_blimp(); };
you should declare this:
namespace Manager { int blimp_count(); void add_more_blimps(int); void destroy_blimp(int); }
The implementation does not need to be exposed to the client like some Heath Rob-inson drawing of marvelous and fascinating complexity. It can be hidden away in the dark recesses of a private implementation file. This has the additional advantage of improving the stability of the file in which the namespace is declared, minimizing large-scale dependent recompilation. Of course, the data used to model the abstrac-tion will not be owned by an object, so it will be static. Beware of the static initializa-tion order fiasco as described above.