C++: How to Provide a Ubiquitous Object Without Including It in Every Parameter List

cglobals

I'm writing a small C++ class, Block, to transform serialized data into a memory structure, and supply the structured data to callers through several accessor methods. I've tried to keep its scope specific and limited. The users of the class are very low-level – they, too, are very narrow in their focus and have as few external dependencies as possible.

This is how I've been taught to engineer things if at all possible. By minimizing dependencies and creeping featurism, it's easier to unit test, and easier to reuse.

The problem is that my class depends upon someone else's class, Metadata. It, too, does one very specific thing: it reads data that defines the characteristics of the data stream I'll be transforming from a database table and passes it to me. His class checks for mySQL errors, which should be rare, and logs any errors to a Log object.

This Log object appears in all of our company's applications. Instantiating it is a big deal – it wants Job numbers, it wants a lot of configuration information from the database that's normally put there in production by account managers using a GUI. You have to do a lot of work before your program can instantiate the Log. Yet this tiny, low-level class (Metadata) with one tiny task wants it to be passed in, by me. My object certainly has no business instantiating the Log, so I have to take it as a parameter from whoever calls me. And so forth, up the calling hierarchy.

I can understand why management wants a class to encapsulate and standardize message logging. But the need for it to be passed to, and through, just about every method is extremely ugly, and makes testing and reuse much more difficult.

This kind of problem must be fairly common. How can this be done without cluttering up the signature of every method you write? Is this a legitimate case for Globals? Is there some kind of Object Oriented approach? And, is this a God object?

Best Answer

Since the logger is some kind of infrastructure it is ok to not pass it around in every call.

In the java and c# world usually every class that need a logger has a static private logger that the class gets from a logger factory

using log4net;
namespace MyNamespace
{
   public class MyClass
   {
      private static readonly ILog log = log4net.LogManager.GetLogger("MyNamespace.MyClass");

      public void someFunction() {
         log.debug("someFunction was called");
      }

This approach assumes that the logger stays always the same instance so the performance overhead is minimal (only one factory call per class).

Alternativly a more dynamic logger that can be changed at runtime could be used through a public static method (singelton)

namespace MyNamespace
{
   public class MyClass
   {
      public void someFunction() {
         Global.getLogger().debug("someFunction was called");
      }

[update 2013-07-14]

answering chaps questions:

In the classlibrary log4net every class gets its own logger instance. The parameter in log4net.LogManager.GetLogger("MyNamespace.MyClass") is the name of the logger. through configuration you can tell the "MyNamespace.MyClass" logger - to be enabled/disabled - that every logging line should be prefixed with "MyNamespace.MyClass"