web-dev-qa-db-ja.com

オープン/クローズの原則

私は オープン/クローズの原則を説明するこのコード例 を見つけました。

原則を適用する前のコード:

_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
}
_

そしてリファクタリングされたコード:

_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
    }
}
_

LoggerクラスをプライベートIMessageLoggerインスタンスで保持する理由を教えてください。私は単にそれを避けるでしょう:

_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
    }
}
_

私が考えることができる唯一の理由は、Loggerクラスを使用した提案された解決策では、このクラスをクライアントコードで引き続き参照できますが、すべてのLog(msg)呼び出しを変更して削除する必要があるためですLogType引数。

2
Majak

新しいLoggerクラスにそれ以上のコードが含まれていない限り、この例は不自然に思われます。しかし、具体的なIMessageLoggerから独立したコードを使用して、クラスがさらにいくつかのメソッドを取得するとします。次に例を示します。

public class Logger
{
    // ...
    public void LogFormatted(string formatString, string[] parameters )
    {
        string message = string.Format(formatString, parameters);
        _messageLogger.Log(message);
    }
}

次に、プログラムをDRYに保ちながら、この追加コードを1か所に実装できるクラスを用意することは理にかなっています。

ここで見るパターンは、古典的な「戦略」パターンです。これは、IMessageLoggerILoggingStrategyに、派生クラスをConsoleLoggingStrategyおよびPrinterLoggingStrategyに名前を変更すると明らかになります。

これはOCPのデモンストレーションでもあります。これは、LoggerIMessageLogger(またはILoggingStrategy)を再利用可能なライブラリ(またはフレームワーク)に入れ、新しいIMessageLoggerの派生は、そのlibを使用するアプリケーションコードに常駐できます。したがって、たとえばLoggerのような新しい戦略のようなものを追加したい場合、CloudLoggingStrategyclassを含むlibを変更する必要はありません。ただし、元のコードでは、Loggerクラスを含むlibをそのような拡張機能に合わせて変更する必要があります。

5
Doc Brown