Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

For a current project I have created a Console class which wraps up C++\'s usual

ID: 659147 • Letter: F

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.

Explanation / Answer

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.

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote