SOLID之开闭原则

2017/05/15 SOLID原则

这是SOLID原则这一系列的第三篇文章,主要来描述开闭(OCP)原则。OCP指出“所有的类或者类似的源代码应该对拓展开放,对修改关闭”。“对拓展开放”意思是你应该设计你的类,以便于有新需求的时候新增新的功能。“对修改关闭”意思是一旦你开发完一个类就不能再修改, 除非是修复bug。   原则的两个部分似乎是矛盾的。可是如果你正确的组织你的类和依赖关系,你无需修改现存代码就可以新增功能。通常你达到这个目的要通过参考抽象依赖关系,比如接口或抽象类而不是使用具体类。 这样的接口一旦开发完就被固定,所以依赖于它们的类可以依赖于不变的抽象。通过创建新的类实现接口添加新功能。应用OCP到你的项目中,一旦源码经过编写、测试以及调试,将被限制修改。这降低了现存代码引入新bug的风险,使得软件更加健壮。使用接口进行依赖的另外一个作用是减少了耦合性,并且增加了灵活性。

示例代码

为了演示OCP的应用,我们可以考虑一些违反它的C#代码,并解释如何重构类遵循以下原则:

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
}

  上述示例代码是用于记录消息的基本模块。 Logger类具有接受要记录的消息和要执行的日志记录类型的单一方法。 switch语句根据程序是否将消息输出到控制台或默认打印机来更改操作。

重构代码

  我们可以轻松重构logging代码,以实现符合OCP原则。首先我们需要移除LogType枚举。因为这会限制可以包含的Logging类型。而不是传递type到logger,我们将为我们需要的每一种消息logger创建一个新类。在最后的代码中,我们将有两个这样的类,命名为“ConsoleLogger”和“PrinterLogger”。之后可以添加其他的logger类型,而无需修改现存的代码。Logger类仍然执行所有日志记录,但使用上述消息记录器类之一来输出消息。为了减少类的耦合性,每个消息类都实现IMessageLogger接口。Logger类从来不知道使用的日志记录的类型,因为它的依赖关系使用构造函数注入作为IMessageLogger实例提供。 重构代码如下:

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

Search

    Table of Contents