Sign in to follow this  
Mantear

Good use of a Singleton?

Recommended Posts

Greetings! I know that those who know better say, whenever possible, not to use Singletons. I'm not doubting those claims, but what I would like to ask if this would be an exception to the rule. Suppose I'm developing a program and would like to add a logging class to it. I'm going to create the logging class myself as a learning excercise to experiment with different methods. My biggest question is how to easily allow virtually any class to log a message. The simplest (or, should I say, hackish) way would be to make the logging class a Singleton. By this point, those who know better than I would say "Don't do that!". I believe one of the arguments is what I may want more than 1 logger at a latter time, which I think is a good argument. However, what if the Singleton I use isn't the logging class itself, but rather a Logging Factory that will create (or simply return a handle to) the type of logger that is desired. This would allow me to create multiple instances of the same logging class as well as different types of loggers. I don't see why you couldn't claim that you will only ever need a single Logger Factory. This way I maintain the ease of access to a logger from anywhere in my program (via the Logger Factory Singleton) as well as the flexibility of creating as many different types of loggers as I want. Are there any drawbacks to this that I am not seeing? Thanks.

Share this post


Link to post
Share on other sites
Geez, not with logging again...

Quote:
I'm not doubting those claims,


Yes, you are.

Quote:
I don't see why you couldn't claim that you will only ever need a single Logger Factory.


<morbo>Singletons do not work that way.</morbo>

The question to answer is: Will the universe implode if you have two factory instances. If answer is yes, then it's justified use of a global. If not, you do not need a singleton.


Quote:
This way I maintain the ease of access to a logger from anywhere in my program


Ah, your real problem is access to logging system, not logging itself. Logging system doesn't need a global. If you choose to use it, fine. But have you even considered the alternatives?

For example, passing either the logging "factory" to objects, or passing "application" or "state" to your objects? For example:

struct Application {
...

LoggingFactory & getLoggingFactory();

...

MemoryManager & getMemoryManager();
};

...

struct MyFoo {
MyFoo(Application & currentContext)
: logger(currentContext.getLoggingFactory())
, buffer(currentContext.getMemoryManager().allocate(1024))
...

};


Here, if you ever need multiple states within same process, it's no problem. Want concurrency, create multiple applications, each running in its own thread, no locks needed.

Share this post


Link to post
Share on other sites
If you use a factory to create your loggers, why would the factory itself need to be a singleton? You could just create an instance of the factory when you need it and have it create a logger for you.

Then, however, you probably don't want to use separate logger each time you want to log something -- unless your loggers maintain some global state (via static variables, for instance) that would prevent you from attempting to open the same file multiple times for writing.

The factory could also keep a list of open loggers and just give you an instance of an already opened logger -- but then this would be just a different implementation of singletons.

It really isn't that hard to pass an instance of a logger wherever you want your logging to be done. This method enables you to define an abstract Logger class from which you can then derive further concrete classes -- like StdoutLogger, FileLogger, SocketLogger, ... Your code then doesn't have to know where the output itself will go -- if anywhere. (NullLogger allows you to elegantly implement --quiet flag to surpress any unnecessary output.) Singletons (or a factory of loggers) wouldn't be much of help here -- your client code would still have to do something like Logger* l; if ( !quiet ) l = loggerFactory.getStdoutLogger(); else l = loggerFactory.getNullLogger();. Your client classes shouldn't really care about wheter they should output anything and where -- just give them an object, have them stuff the output into it, possibly specifying how important the message is, and let the logger take care of the details.

Share this post


Link to post
Share on other sites
The logger is a good example of a good use of a global variable, because a bunch of different classes will want to access it even if it's not technically "their job". It is a good example of a bad use of a singleton, since it restricts users from creating a second logger even when that's really, really what they want to do.

Share this post


Link to post
Share on other sites
The Singleton pattern ensures there will only ever be a single instance of a class. Most often you do not need this property since in many cases it is absolutely legal to have more than one instance of the same class. And if not you can simply just create only a single instance.

If you need global access to your logger and don't want to pass the instance to every class/function etc. that needs it you can simply use a global variable. Some may now scream about the use of global variables, but IMO justified use of globals is not bad at all. Just look at std::cout etc.; they are globals too.

Share this post


Link to post
Share on other sites
Like Sneftel said, you can certainly argue that a logger, or a log factory, should be global.

But why prevent me from creating two of them?
What do you gain by adding this constraint? Why not just make it global?
Then I still have the option of using your "default" logger factory, or I can create my own if that's what I need.

Quote:
I don't see why you couldn't claim that you will only ever need a single Logger Factory.

Presumably, the reason you have a factory, rather than just calling the log constructor, is that it contains some kind of state, some configuration settings.
And if it does that, then it is conceivable that I might want to create a log based on different configuration settings. Which would imply a second factory.

And singletons are not about "You'll only ever need one". They mean "It is an error to create more than one".
Is there any reason why it would cause problems to have multiple logs or multiple logger factories? If so, you may need to enforce that only one instance can exist. If not, there's no call for a singleton.

Share this post


Link to post
Share on other sites
A logging utility may not always be a valid candidate for "singletonizing". My project has a client and server side that run within the same application. I would want separate logging for both the server and client side to be able to troubleshoot better.

Quote:
Original post by Antheus
Quote:
This way I maintain the ease of access to a logger from anywhere in my program


For example, passing either the logging "factory" to objects, or passing "application" or "state" to your objects?


I think everybody agrees with your comment that one SHOULD pass around pointers, but what I always struggle with is where to draw the line. When does passing around pointers become more trouble than it's worth? And when you put everything in an "Application" object, do you not expose too much of your system to classes that should not be able to touch parts of the system?


Personally it bothers me when I have to pass around a lot of utility objects to classes. This means that when I write unit tests for example I have to initialize or stub a lot of classes. There is logging, memory management, but I also have performance utility classes for data throughput and timing.

For now I chose for some of these to be completely static classes, just for ease of use. Especially the performance utilities you don't want to pass around. These are only used in very few classes.
I haven't used these performance utilities in unit tests yet, so it may turn out that having multiple instance of them is required.



One way to ease the pain of passing around a tonne of pointers would indeed be to use the "Application" object that contains all these utilities, but somehow it doesn't "feel" right. It may become very tempting to put classes in that "Application" that are only used by a few classes. For example, you can put the "World" object in the "Application" because all the scene objects use it.



I usually go with the flow of the project and code, do what is easiest at the moment. But what I'm wondering is what is your approach Antheus? Are you very strict in this? Do you pass around all those pointers through all layers of your application? Do you pass around an Application object? Or are you more pragmatic in this?

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
The question to answer is: Will the universe implode if you have two factory instances. If answer is yes, then it's justified use of a global. If not, you do not need a singleton.
Quote:

Logging system doesn't need a global.


The question to answer is: Will the universe implode if you have a single global in your app? What's the taboo about this?!

In my opinion, Logger is a good use of singleton. Logger is so widely used that passing the object around clutters the code to a degree beyond my imagination.

Call me the worst programmer on the planet, but I even justify the use of goto under rare circumstances, let alone singletons. [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by Ashkan
In my opinion, Logger is a good use of singleton. Logger is so widely used that passing the object around clutters the code to a degree beyond my imagination.

You've just made the most common singleton mistake: Assuming that a singleton is for the purpose of providing global access to an object. Globals and singletons are not created alike: one is for allowing global access, and the other is for restricting creation.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by Ashkan
In my opinion, Logger is a good use of singleton. Logger is so widely used that passing the object around clutters the code to a degree beyond my imagination.

You've just made the most common singleton mistake: Assuming that a singleton is for the purpose of providing global access to an object. Globals and singletons are not created alike: one is for allowing global access, and the other is for restricting creation.


Brilliant! You got me by the balls. You're absolutely correct.

/EDIT: Too bad you changed your post before I reply. Did globals hurt me as a child? [smile]

Share this post


Link to post
Share on other sites
Just do what's easier for you at the moment, I always try to get my projects up and running fully before worrying about these sort of things. I pretty much use Singletons whenever I want access to something globally. A few examples: messaging systems, logging systems, sound systems. I'm pretty sure that's not the correct use of singletons but for me it beats passing around pointers like crazy cluttering the code.

Share this post


Link to post
Share on other sites
Quote:
I pretty much use Singletons whenever I want access to something globally.

Doesn't this kind of contradict the idea of doing the simplest possible thing. The simplest is definitely to have a global, and then if subsequent analysis shows that creating more than one object would be an error you can add singleton functionality. Some applications should have global access to logging, but no applications whatsoever should restrict you to one logging system.

Share this post


Link to post
Share on other sites
We were implementing an AI system, which needed to integrate with our existing action management system. There are three interdependent state machines.

The individual states follow the visitor pattern, allowing each actor-type (derived from a base actor) to handle their own behaviors. Each state is derived from a base state, and stored within the individual actors.

Every class must be addressable, so there must be an actual instance of them somewhere. These state classes have no data, essentially just "bundles of virtual functions". They are retrieved through a factory.

Each "bundle of functions" is a const singleton. They are retrieved through a factory method. The only viable alternative to this approach is to use a bunch of really big switch statements, which would be an evil design.

Share this post


Link to post
Share on other sites
Quote:
Original post by frob
Each "bundle of functions" is a const singleton. They are retrieved through a factory method. The only viable alternative to this approach is to use a bunch of really big switch statements, which would be an evil design.

What about the alternative where the factory returns a new State object each time? If there's no data members, then functionally and performance-ly the two approaches would be identical.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by frob
Each "bundle of functions" is a const singleton. They are retrieved through a factory method. The only viable alternative to this approach is to use a bunch of really big switch statements, which would be an evil design.

What about the alternative where the factory returns a new State object each time? If there's no data members, then functionally and performance-ly the two approaches would be identical.

Not quite.

That approach consumes substantially more memory. Comparing states requires more processing (which is trivially avoided with the singleton requirement).

Also, we are on a very small system. Our memory quotas are usually in bytes, not kilobytes, and even a few extra cycles appears on our profiling. In this world, there *is* a measurable performance difference and a meaningful memory difference between the approaches.

Even on a PC, the performance is not identical, there is still a (on the PC it is extremely minor) cost for allocating, creating, destroying, and freeing each new object. There is a mental overhead required in keeping track of the object lifetimes that isn't there with the singleton approach.

[edit] Also, it is a bad habit to create multiple instances of identical immutable objects. Imagine the situation of C#'s String.Empty vs. the string "". The difference is extremely small, but if you need to create a lot of them, it adds up.

Share this post


Link to post
Share on other sites
frob, what you describe is not really a singleton even though the factory method always returns a pointer to the same underlying object. I believe this is more like the flyweight pattern. There is nothing in the system that would prevent you from returning a pointer to a newly created object instead of always the same object.

Share this post


Link to post
Share on other sites
Quote:
Original post by frob
That approach consumes substantially more memory.

How so? Each object consists of exactly one vtable pointer. How will having several pointers to one object be a win?

Share this post


Link to post
Share on other sites
Good Globals vs. Bad Globals.

Why is one global good and another bad? It has to do with what the global is doing and who can make changes to data in global. A bad global contains data that can be used by and can be changed by all the classes in the application. Why is this bad? Well it is a debugging nightmare at best and disaster waiting to happen is more likely. Your program every now and then gets bad data from this class, how do you track it down? You have to look at every class that is making changes and try to find the one that is messing up. Really ugly.

So what is a good global? It is one that may have data that all the classes use but can only be changed by one class (we won't get into how you can do that). The other is a global that does not contain data but just helps do things (logging is a great example).

theTroll

Share this post


Link to post
Share on other sites
A global variable is 'bad' when it is mutable and the behavior of unrelated portions of code rely on its state. In this respect, a logger is a 'good' global variable, because the behavior of code does not rely on the state of the logger even though it is mutable.

As for the four relevant questions to be asked when considering a singleton (including the one mentioned above), follow the "Singleton" link in my signature.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by frob
That approach consumes substantially more memory.

How so? Each object consists of exactly one vtable pointer. How will having several pointers to one object be a win?

For this situation, lets assume some simple numbers:

vtable size: 80 bytes.
pointer size: 4 bytes.

In the first case of pointing to an immutable singleton, only one vtable exists. Every object requires a single pointer to the singleton. If we assume 255 actors, that is a total of 4*255 + 80*numstates bytes.

In the second case with each being unique instances, no static vtables exist. Again with 255 actors, we consume 80*255 bytes.

So (4*255 + 80*numstates) on one side, (80*255) on the other. As long as we have fewer than 242 states in the state machine, we have saved memory. In this particular case, we saved about 18KB. Multiply it by three interrelated state machines, and we save about 50KB. Consider the additional code cost for manipulating instances rather than assuming all unique addresses are unique states, and equivalent addresses are equivalent states, the additional creation and shutdown code, etc., and then you are up to around 100KB in combined code and data sizes.

So the choice is unique instances for about 100KB code+data, or singleton instances for about 3KB code+data.

As I said, on a modern PC the 100KB and small amount of overhead are not much. It is small, but they also aren't free, and certainly not "performance-ly" identical as you put it.

Unique instances require over 2% of the total memory on this system, whereas a collection of singletons requires 0.08% of the total memory. In this situation, singletons are clearly the better choice.

Share this post


Link to post
Share on other sites
Objects don't contain vtables. They contain pointers to vtables. There is only one vtable for a class, no matter how many objects of that class you create. Try a sizeof if you don't believe me.

Share this post


Link to post
Share on other sites
I use to love singletons. Now I hate them, because they make refactoring an utter nightmare. I find globals to be no better. If there is an excuse for globals, it's for that single application object, since if you go with more than one, you're entering the realm of undefined global initialization order.

The problem I have is even having a single global absolutely irks me, because now when I want to pull code that happens to use the logging facility into a unit test, I have to bring all this other crap along for the ride. So, let's forget about singletons for the moment and strictly talk globals. Am I alone here in thinking even an application object should not be global?

Share this post


Link to post
Share on other sites
It's certainly possible to write an application that doesn't use globals, and that doesn't use big catchall pass-me-to-everything structs that pretend not to be full of globals. It is not necessarily easy, and the tradeoffs involved are not necessarily worth it. There's a widespread misconception these days that only object-oriented code is worth writing or capable of being maintainable and robust. This state of affairs helps cookie-cutter Java school graduates, OO textbook authors, and very few other people. In point of fact, a pure OO approach is usually not as quick to write or as easy to read as a blended approach, and (particularly for projects with less than 100,000 LoC or so) not necessarily ideal as a dominant paradigm.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this