multipart-mixed

Mini-Pattern: Making Static Methods Semi-Polymorphic

There are some global mechanisms where it's common to use static (class) methods as a primary API, only to discover later that the mechanism needs requires polymorphic behavior. For example, a logging mechanism may simply use printf() to start with, but later it may require changeable behavior depending on command-line arguments. This mini-pattern shows how to make a static method API polymorphic under the covers.

Applicability

Applicable in many places using static methods or globals, e.g. logging and factory methods.

Motivation

Consider a simple C++ console class. Rather than using a global (since we're told that globals are evil) or using inline printf() everywhere (since that really limits our options), we make a class with some static methods:

class Console
{
  public:
    static void log(char* message)
    {
        printf("** %s\n", message);
    }
};

int main()
{
    Console::log("hello world");
    return 0;
}

[Note that I'm showing all methods inline and skipping varargs for the log() method.] This will certainly do for a while. Later if we need to log to a file, we can change Console::log() to do that without changing the calling code.

But then let's say the user needs an option to choose between several types of output:

  • Print to screen (default).
  • Print to screen and mirror to trace file.
  • Print to screen and cache in circular memory queue, flush to trace file on request.

Now our humble Console::log() static method starts getting nasty. We could put a bunch of if..else clauses in there, and for this example that might work fine. Let's say Console is more complicated than that, and the most appealing option is creating specialized subclasses of Console. But how do we use a subclass when all our application code is specifically calling Console::log()?

In order to keep the client API the same, we need to resort to some under-the-covers polymorphism.

Implementation

Create a protected static pointer to the console, and a protected helper method which does the actual work, then have Console::log() use that:

class Console
{
  public:
    static void log(char* message)
    {
        assert(console != 0);
        console->logHelper(message);
    }

    static void setConsole(Console *newConsole)
    {
        if (console != 0)
            delete console;
        console = newConsole;
    }    

  protected:
    virtual void logHelper(char* message) = 0;
    static Console* console;
};

Console* Console::console = 0;

Now we can create and use some subclasses of Console. In the interest of simplicity, I'll create some subclasses which just use different message prefixes (asterisks or dashes):

class AsteriskConsole : public Console
{
  protected:
    virtual void logHelper(char* message)
    {
        printf("** %s\n", message);
    }
};

class DashConsole : public Console
{
  protected:
    virtual void logHelper(char* message)
    {
        printf("-- %s\n", message);
    }
};

int main()
{
    // ...

    if (userWantsDashes)
        Console::setConsole(new DashConsole);
    else
        Console::setConsole(new AsteriskConsole);

    Console::log("hello world");
    return 0;
}

Consequences:

The advantages of this mini-pattern are:

  • Class and static methods easy to create upfront, relatively easy to extend with minimal (if any) changes to client code.
  • Management of the current console is contained within the Console class.

The disadvantages:

  • More code to write upfront than just having a global pointer to the subclass of your choice.
  • Slight performance hit (double function call instead of one), unless you inline key methods.