Question

For a current project I have created a Console class which wraps up C++'s usual cout, writes output to a file or uses Console::WriteLine depending on the environment. Currently I've setup my code base to inject a console object into each class that I use, making all of them extend a base class: Debuggable. This seemed like a great plan at first but now that I have to pass the console to every object and ensure that it is created before all of them I'm having second thoughts.

Usually consoles seem to be handled with static objects, so I'm considering changing my implementation to use a static instance of the console that all the objects can use but I feel that this removes an element of control, I can't have more than one potential console with different formats and targets.

I understand that there's a lot of discussion about dependency injection, I'm interested specifically in the case of utilities such as a console.

Was it helpful?

Solution

Usually when there is an object, which needs to be used everywhere (like your logger class), then using the singleton makes sense. And that is one of rare cases where the singleton is not an anti pattern.

You can implement it in such case that it is possible to create a mock (if you going to unit tests).

So, something like this :

class Logger
{
public:

    typedef Debugable* (Fnc)();

    static Debugable& Instance()
    {
        static Debugable* p = f();
        return *p;
    }

    void SetCreator( Fnc newf ){ f = newf; }


private:
    static Debugable* DefaultCreator(){ return 0; }
    static Fnc f = DefaultCreator();
};

First you need to set the creator function somewhere (do this only once) :

Debugable* CreateLogger()
{
   return new StdOutputLogger;
};

//...
// somewhere
Logger::SetCreator( CreateLogger );

then use it as a singleton : Logger::Instance() << "log this message";

In unit tests, you need to create a mock object :

struct MockLogger : public Debugable { // methods };

Set it in the setUp() function :

MockLogger mock;
Debugable* CreateMock()
{
   return new StdOutputLogger;
};

void setUp()
{
  Logger::SetCreator( CreateMock );
}

then check the accesses to the mock.

OTHER TIPS

Note: Initially I answered this question without realizing that it is about the use of singletons for debugging purposes. So, the first part of my answer is for using singletons in general, not for debugging. The second part of my answer addresses the issue of debugging. (I have added to it a couple of points I made in comments.)


Part 1 - singletons in general (without respect to debugging)

My recommendation would be to not go with a singleton. My experience is that "the singleton" is not a pattern, but an anti-pattern by its very nature. (But let's not turn this into yet one more episode of the singleton war, shall we?)

You say that you are "interested specifically in the case of utilities such as a console." I am afraid that a console is not a utility. It is a stream to which you send output.

  • Text sent to it gets buffered, so it has state.

  • Concurrently calling it from multiple threads might not crash it, but it will result in misplaced linefeeds and garbled lines, so it cannot really be thought of as amenable to multi-threading.

  • Even if it is convenient for some to think of it as a singleton, conceptually it is not, as evidenced by the fact that you can redirect it to other devices like files, printers, etc.

So, either you can treat it as what it really is, (an output stream, passed to whoever needs to output stuff,) or treat it like some special public global one-of-a-kind thing, the nature of which everyone has intimate knowledge of, and then rely on the sorcery provided by the library and the OS beneath the scenes to undo your assumptions and make it work like the output stream that it really is, as far as the rest of the system is concerned.

True, you may end up making it a singleton due to pragmatic concerns of convenience vs. correctness, but I suppose that if convenience was your primary concern then you would not be asking the question in the first place. So, this answer is to say that there do in fact exist other engineers besides you, like me, who will tenaciously pursue the "do it right, avoid singletons" approach.


Part 2 - singletons for debugging

I seem to have missed the point about debugging in the original question. When it comes to debugging, I do not have objections with the use of singletons. I would use a singleton as a debugging helper, I would not use a singleton for anything else. But then again, we do not test debugging helpers. If it requires testing, then it is not just a debugging helper.

In the past I have tried passing debugging facilities as dependencies to objects in the same way that I pass formal dependencies (dependencies that that are part of the design) and I have come to the conclusion that a) it is a big pain, b) it adds more complexity than the design calls for. When debugging dependencies are treated with the same dignity that design dependencies are treated, they start to appear as if they are part of the design, while they are not.

For example, say you have a collection which is sortable and you want to pass it a comparator. Is it really worth the hassle to make the comparator an object so that you can inject the debugging dependency in it? And isn't it kind of odd now that the comparator has become an entity of its own, as if it is part of the design, while in fact the only reason it is an entity is so as to have a reference to the debugging facility?

So, if it is only for debugging, (which, incidentally, means that it will not require testing,) then go ahead and use a singleton, it is fine.

Licensed under: CC-BY-SA with attribution
scroll top