C#设计模式六大原则 - 开闭原则

开闭原则(OCP)

Open Closed Principle,简称:OCP

一个软件实体应当对扩展开放,对修改封闭

问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。

解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

一个软件或系统在开发的过程中,以及上线生产后,随着时间的推移,都会产生变化,这是铁定的事实。那我们在设计时就要尽量适应这些变化,来提高系统的稳定性和灵活性,开闭原则就是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。

开闭原则指导我们如何建立一个稳定的、灵活的系统。

开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。开闭原则可能是设计模式六项原则中定义最模糊的一个了,它只告诉我们对扩展开放,对修改关闭,可是到底如何才能做到对扩展开放,对修改关闭,并没有明确的告诉我们。

开闭原则就是一个目标,其它五大原则都是实现手段,它就像一个口号,你们都要奔着这个口号来。要满足开闭原则,就需要对系统进行抽象化设计,抽象化是开闭原则的关键,换句说法就是对系统进行抽象约束

开闭原则的重要性

开闭原则对测试的影响:

开闭原则可是保持原有的测试代码仍然能够正常运行,我们只需要对扩展的代码进行测试就可以了。

开闭原则可以提高复用性:

在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。只有这样代码才可以复用,粒度越小,被复用的可能性就越大。

开闭原则可以提高可维护性:

面向对象开发的要求

如何使用开闭原则

1、抽象约束
  • 通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;
  • 参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;
  • 抽象层尽量保持稳定,一旦确定即不允许修改。
2、元数据控制模块行为

在实际开发中,要尽量使用注解或者配置文件来控制程序的行为,减少重复开发。

3、约定优于配置

在一个团队中,建立项目章程是非常重要的,因为章程中指定了所有人员都必须遵守的约定,对项目来说,约定优于配置。

4、封装变化

对变化的封装包含两层含义:
第一,将相同的变化封装到一个接口或者抽象类中;
第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。

举个栗子~

这个太抽象了,就不举栗了吧!!哈哈哈哈……(有空碰到合适的例子再更~)

时隔数月,还是看到比较合适的例子,首先来看一个违反OCP的代码。

public class ErrorLogger
{ 
    private readonly string _whereToLog;
    public ErrorLogger(string whereToLog)
    { 
        this._whereToLog = whereToLog.ToUpper();
    }

    public void LogError(string message)
    { 
        switch (_whereToLog)
        { 
            case "TEXTFILE":
                WriteTextFile(message);
                break;
            case "EVENTLOG":
                WriteEventLog(message);
                break;
            default:
                throw new Exception("Unable to log error");
        }
    }

    private void WriteTextFile(string message)
    { 
        System.IO.File.WriteAllText(@"C:\Users\Public\LogFolder\Errors.txt", message);
    }

    private void WriteEventLog(string message)
    { 
        string source = "DNC Magazine";
        string log = "Application";
        
        if (!EventLog.SourceExists(source))
        { 
            EventLog.CreateEventSource(source, log);
        }
        EventLog.WriteEntry(source, message, EventLogEntryType.Error, 1);
    }
}

如果您需要添加新的日志记录位置(例如数据库或Web服务),会发生什么情况?您需要在几个地方修改此代码。您还需要添加新代码,以将消息写入新位置。最后,您必须修改单元测试以考虑新功能。所有这些类型的更改都有可能引入错误。

您可能已经错过了此代码的另一个问题。它违反了单一责任原则。

接下来是更好的方法。虽然功能不完整,但这是一个起点。

public interface IErrorLogger
{ 
    void LogError(string message);
}

public class TextFileErrorLogger : IErrorLogger
{ 
    public void LogError(string message)
    { 
        System.IO.File.WriteAllText(@"C:\Users\Public\LogFolder\Errors.txt", message);
    }
}

public class EventLogErrorLogger : IErrorLogger
{ 
    public void LogError(string message)
    { 
        string source = "DNC Magazine";
        string log = "Application";

        if (!EventLog.SourceExists(source))
        { 
            EventLog.CreateEventSource(source, log);
        }

        EventLog.WriteEntry(source, message, EventLogEntryType.Error, 1);
    }
}

当您需要实现其他类型的记录器时,这很简单……只需添加新类,而不要修改现有类。

public class DatabaseErrorLogger : IErrorLogger
{ 
    public void LogError(string message)
    { 
        // 将错误消息写入数据库的代码
    }
}

public class WebServiceErrorLogger : IErrorLogger
{ 
    public void LogError(string message)
    { 
        // 将错误消息写入Web的代码服务
    }
}
    原文作者:familychase
    原文地址: https://blog.csdn.net/qq_34482621/article/details/103616713
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞