• 设计模式之美--开闭原则


    什么是开闭原则?

    开闭原则的英文是Open Closed Principle,简称OCP,它的英文描述是:

    Software entities (modules, classes, functions, etc.) should be open for extension, but closed for modification.

    软件实体(模块、类、方法等)应该『对扩展开放、对修改关闭』。

    通俗的说,在添加一个新的功能时,应该是在已有的代码基础上扩展代码(如新增模块、类、方法等),而非修改已有的代码。

    举个栗子:

    下面是一个API接口监控警告的代码,AlertRule存储警告规则,Notification是告警通知类。业务逻辑主要集中在check()函数。

    public class Alert {
      private AlertRule rule;
      private Notification notification;
    
      public Alert(AlertRule rule, Notification notification) {
        this.rule = rule;
        this.notification = notification;
      }
    
      public void check(String api, long requestCount, long errorCount, long durationOfSeconds) {
        long tps = requestCount / durationOfSeconds;
        if (tps > rule.getMatchedRule(api).getMaxTps()) {
          notification.notify(NotificationEmergencyLevel.URGENCY, "...");
        }
        if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
          notification.notify(NotificationEmergencyLevel.SEVERE, "...");
        }
      }
    }
    

    现在,如果我要添加一个功能:当每秒接口超时请求个数,超过某个预先设置的最大阈值时,就报警。 我们可以直接在check()函数里修改入参,添加timeoutCount,然后在函数内部添加if分支。

    但如果后面需要添加的规则越来越多,就多导致函数的参数很多,管理其他很麻烦。

    我们可以重构一下:

    • check()函数的多个入参封装为ApiStatInfo类

    • 引入handler的概念,将if逻辑分散在各个handler

    public class Alert {
      private List<AlertHandler> alertHandlers = new ArrayList<>();
      
      public void addAlertHandler(AlertHandler alertHandler) {
        this.alertHandlers.add(alertHandler);
      }
    
      public void check(ApiStatInfo apiStatInfo) {
        for (AlertHandler handler : alertHandlers) {
          handler.check(apiStatInfo);
        }
      }
    }
    
    public class ApiStatInfo {//省略constructor/getter/setter方法
      private String api;
      private long requestCount;
      private long errorCount;
      private long durationOfSeconds;
    }
    
    public abstract class AlertHandler {
      protected AlertRule rule;
      protected Notification notification;
      public AlertHandler(AlertRule rule, Notification notification) {
        this.rule = rule;
        this.notification = notification;
      }
      public abstract void check(ApiStatInfo apiStatInfo);
    }
    
    public class TpsAlertHandler extends AlertHandler {
      public TpsAlertHandler(AlertRule rule, Notification notification) {
        super(rule, notification);
      }
    
      @Override
      public void check(ApiStatInfo apiStatInfo) {
        long tps = apiStatInfo.getRequestCount()/ apiStatInfo.getDurationOfSeconds();
        if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()) {
          notification.notify(NotificationEmergencyLevel.URGENCY, "...");
        }
      }
    }
    
    public class ErrorAlertHandler extends AlertHandler {
      public ErrorAlertHandler(AlertRule rule, Notification notification){
        super(rule, notification);
      }
    
      @Override
      public void check(ApiStatInfo apiStatInfo) {
        if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).getMaxErrorCount()) {
          notification.notify(NotificationEmergencyLevel.SEVERE, "...");
        }
      }
    }
    

    重构之后,可以通过ApplicationContext单例类,对Alert进行创建和组装。

    public class Alert { // 代码未改动... }
    public class ApiStatInfo {//省略constructor/getter/setter方法
      private String api;
      private long requestCount;
      private long errorCount;
      private long durationOfSeconds;
      private long timeoutCount; // 改动一:添加新字段
    }
    public abstract class AlertHandler { //代码未改动... }
    public class TpsAlertHandler extends AlertHandler {//代码未改动...}
    public class ErrorAlertHandler extends AlertHandler {//代码未改动...}
    // 改动二:添加新的handler
    public class TimeoutAlertHandler extends AlertHandler {//省略代码...}
    
    public class ApplicationContext {
      private AlertRule alertRule;
      private Notification notification;
      private Alert alert;
      
      public void initializeBeans() {
        alertRule = new AlertRule(/*.省略参数.*/); //省略一些初始化代码
        notification = new Notification(/*.省略参数.*/); //省略一些初始化代码
        alert = new Alert();
        alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));
        alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));
        // 改动三:注册handler
        alert.addAlertHandler(new TimeoutAlertHandler(alertRule, notification));
      }
      //...省略其他未改动代码...
    }
    
    public class Demo {
      public static void main(String[] args) {
        ApiStatInfo apiStatInfo = new ApiStatInfo();
        // ...省略apiStatInfo的set字段代码
        apiStatInfo.setTimeoutCount(289); // 改动四:设置tiemoutCount值
        ApplicationContext.getInstance().getAlert().check(apiStatInfo);
    }
    

    开闭原则有什么好处?

    开闭原则实际上是对基于接口或抽象实现『封闭』,基于实现接口或继承实现『开放』的阐释。对扩展开发是为了应为需求变化,对修改关闭时为了保证已有代码的稳定性,最终让系统更具有弹性。

    开闭原则并不是免费的,有些情况下,代码的扩展性与可读性是互相冲突的。在某些场景下,代码的扩展性很重要,我们就可以适当牺牲一些代码可读性;在另一些场景下,代码的可读性更重要,我们就需要牺牲一定的代码可扩展性。

    如何践行开闭原则?

    • 时刻具备扩展意识、抽象意识、封装意识

    • 往前多思考、预先留好扩展点

    • 识别可变和不可变部分,将可变部分封装,隔离变化,提供抽象的不可变接口

    • 许多设计原则、设计思想、设计模式都是为提高代码扩展性而生的

    回顾一下

    开闭原则的英文是Open Closed Principle,简称OCP。在添加一个新的功能时,应该是在已有的代码基础上扩展代码(如新增模块、类、方法等),而非修改已有的代码。我们要时刻具备扩展意识、抽象意识、封装意识,识别可变和不可变部分,将可变部分封装,隔离变化,提供抽象的不可变接口。

  • 相关阅读:
    JSON:JSON对象和JSON数组混排的复杂字符串
    爬虫4:pdf页面+pdfminer模块+demo
    爬虫3:html页面+webdriver模块+demo
    爬虫2:html页面+beautifulsoap模块+post方式+demo
    爬虫1:html页面+beautifulsoap模块+get方式+demo
    IKanalyzer、ansj_seg、jcseg三种中文分词器的实战较量
    【转】linux下如何查看某个软件 是否安装?安装路径在哪
    Linux下通过源码编译安装程序
    【转】rpm包和源码包安装的区别
    linux centos7 安装nginx并启动
  • 原文地址:https://www.cnblogs.com/CocoML/p/12817449.html
Copyright © 2020-2023  润新知