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.