I found this code example explaining Open / Closed principle.
Code before application of principle:
public class Logger
{
public void Log(string message, LogType logType)
{
switch (logType)
{
case LogType.Console:
Console.WriteLine(message);
break;
case LogType.File:
// Code to send message to printer
break;
}
}
}
public enum LogType
{
Console,
File
}
And refactored code:
public class Logger
{
IMessageLogger _messageLogger;
public Logger(IMessageLogger messageLogger)
{
_messageLogger = messageLogger;
}
public void Log(string message)
{
_messageLogger.Log(message);
}
}
public interface IMessageLogger
{
void Log(string message);
}
public class ConsoleLogger : IMessageLogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class PrinterLogger : IMessageLogger
{
public void Log(string message)
{
// Code to send message to printer
}
}
Can you explain me the reason to still keep Logger
class with private IMessageLogger
instance? I would simply avoid it by:
public interface ILogger
{
public void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class PrinterLogger : ILogger
{
public void Log(string message)
{
// Code to send message to printer
}
}
The only reason I can think about is, that in suggested solution with Logger
class, we could still refer to this class in client code, but we still need to modify all Log(msg)
calls to remove LogType
arguments.
Best Answer
As long as the new
Logger
class does not contain more code, the example seems to be contrived. But imagine that class gets some more methods, with code which is independent from the concreteIMessageLogger
, for example:Then it makes sense to have a class where you can implement this additional code in one place, keeping the program DRY.
The pattern we see here is the classic "strategy" pattern. This becomes obvious when you rename
IMessageLogger
toILoggingStrategy
, and the derived classes toConsoleLoggingStrategy
andPrinterLoggingStrategy
.It is also a demonstration of the OCP, because now you can put
Logger
andIMessageLogger
(orILoggingStrategy
) into a reusable library (or framework), whilst newIMessageLogger
derivations can reside in application code using that lib. So the lib, including theLogger
class, won't have to be changed if someone wants to add something like a new strategy likeCloudLoggingStrategy
, for example. In the original code, however, a lib containing theLogger
class would have to be changed for such an extension.