设计模式--设计原则--开闭原则

定义

在面向对象编程领域中,开闭原则规定软件中的对象、类、模块和函数对扩展是开放的,但对于修改是封闭。这应该用抽象定义结构,用具体实现拓展细节。

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

模拟案例

场景:API 接口监控告警功能

不同的预警有不同的紧急程度的通知

  1. 通知的紧急程度,包括 SEVERE(严重)、URGENCY(紧急)、NORMAL(普通)、TRIVIAL(无关紧要)。
  2. 预警触发规则

当接口的 TPS 超过某个预先设置的最大值时,发送紧急通知

当接口请求出错数大于某个最大允许值时,发送严重通知

新加一个规则

当每秒钟接口超时请求个数,超过某个预先设置的最大阈值时,发送严重通知

基础类

java 复制代码
/**
 * @description:紧急程度枚举
 * @version: 1.0
 * @author:blackcat
 */
public enum NotificationEmergencyLevel {

    /**
     * 普通
     */
    NORMAL,
    /**
     * 紧急
     */
    URGENCY,
    /**
     * 严重
     */
    SEVERE,

    /**
     * 无关紧要
     */
    TRIVIAL;

}
java 复制代码
/**
 * @description:通知模拟类
 * @version: 1.0
 * @create:2023/11/3 10:14
 * @author:blackcat
 */
@Slf4j
public class Notification {

    /**
     *
     * @param urgency
     * @param msg
     */
    public void notify(NotificationEmergencyLevel urgency, String msg) {

        log.info("等级:{},内容:{}",urgency,msg);
    }
}
java 复制代码
/**
 * @description:规则属性
 * @version: 1.0
 * @author:blackcat
 */
public class Rule {

    /**
     * 最大tps阈值
     */
    private long maxTps;

    /**
     * 最大错误次数阈值
     */
    private long maxErrorCount;


    public Rule(long maxTps, long maxErrorCount) {
        this.maxTps = maxTps;
        this.maxErrorCount = maxErrorCount;
    }

    public long getMaxTps() {
        return maxTps;
    }

    public void setMaxTps(long maxTps) {
        this.maxTps = maxTps;
    }

    public long getMaxErrorCount() {
        return maxErrorCount;
    }

    public void setMaxErrorCount(long maxErrorCount) {
        this.maxErrorCount = maxErrorCount;
    }
}
java 复制代码
/**
 * @description:告警规则集
 * @version: 1.0
 * @author:blackcat
 */
public class AlertRule {

    private Map<String, Rule> ruleMap = new HashMap<>();


    public AlertRule() {
        register("/api/user/add", new Rule(100L, 10L));
        register("/api/user/list", new Rule(1000L, 5L));
    }

    public void register(String api, Rule rule) {
        ruleMap.put(api, rule);
    }


    public Rule getMatchedRule(String api) {
        return ruleMap.get(api);
    }
}
java 复制代码
/**
 * @description:api相关信息
 * @version: 1.0
 * @author:blackcat
 */
@Data
public class ApiInfo {
    /**
     * 请求api路径
     */
    private String api;
    /**
     * 请求次数
     */
    private long requestCount;
    /**
     * 错误次数
     */
    private long errorCount;

    /**
     * 单位时间
     */
    private long durationOfSeconds;

    /**
     * 新增:接口超时次数
     */
    private long timeoutCount;
}


/**
 * @description:告警功能实现
 * @version: 1.0
 * @author:blackcat
 */
public class Alert {

    /**
     * 存储告警规则
     */
    private AlertRule rule;
    /**
     * 告警通知类
     */
    private Notification notification;

    public Alert(AlertRule rule, Notification notification) {
        this.rule = rule;
        this.notification = notification;
    }



    public void check(ApiInfo apiInfo) {
        String api = apiInfo.getApi();
        long requestCount = apiInfo.getRequestCount();
        long durationOfSeconds = apiInfo.getDurationOfSeconds();
        long errorCount = apiInfo.getErrorCount();
        long tps = requestCount / durationOfSeconds;

        //当接口的 TPS 超过某个预先设置的最大值时,发送通知
        if (tps > rule.getMatchedRule(api).getMaxTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "TPS超过阈值发送紧急预警");
        }
        //当接口请求出错数大于某个最大允许值时,发送通知
        if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
            notification.notify(NotificationEmergencyLevel.SEVERE, "接口请求出错数超过阈值发送严重预警");
        }
    }
}

违背开闭原则实现

java 复制代码
@Data
public class ApiInfo {

    /**
     * 改动一:新增:接口超时次数
     */
    private long timeoutCount;
}

public class Alert {



    public void check(ApiInfo apiInfo) {
        //改动二  新增 当每秒钟接口超时请求个数超过阈值发送紧急预警"
        long timeoutCount = apiInfo.getTimeoutCount();
        long timeoutTps= timeoutCount / durationOfSeconds;
        if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "当每秒钟接口超时请求个数超过阈值发送紧急预警");
        }
    }
}

改动一:ApiInfo:新增属性:接口超时次数 改动二:Alert的check()方法添加相关逻辑 改动三:Rule添加单位时间内最大超时次数阈值属性, AlertRule赋值单位时间内最大超时次数阈值属性

开闭原则优化

抽象告警具体器

java 复制代码
/**
 * @description:告警处理器抽象
 * @version: 1.0
 * @author:blackcat
 */
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(ApiInfo apiInfo);
}



/**
 * @description: 接口请求出错数的告警处理器
 * @version: 1.0
 * @author:blackcat
 */
public class ErrorAlertHandler extends AlertHandler {

    public ErrorAlertHandler(AlertRule rule, Notification notification) {
        super(rule, notification);
    }

    @Override
    public void check(ApiInfo apiInfo) {
        String api = apiInfo.getApi();
        long errorCount = apiInfo.getErrorCount();
        //当接口请求出错数大于某个最大允许值时,发送通知
        if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
            notification.notify(NotificationEmergencyLevel.SEVERE, "接口请求出错数超过阈值发送严重预警");
        }
    }
}


/**
 * @description: TPS的告警处理器
 * @version: 1.0
 * @author:blackcat
 */
public class TpsAlertHandler extends AlertHandler {

    public TpsAlertHandler(AlertRule rule, Notification notification) {
        super(rule, notification);
    }

    @Override
    public void check(ApiInfo apiInfo) {
        String api = apiInfo.getApi();
        long requestCount = apiInfo.getRequestCount();
        long durationOfSeconds = apiInfo.getDurationOfSeconds();
        long tps = requestCount / durationOfSeconds;

        //当接口的 TPS 超过某个预先设置的最大值时,发送通知
        if (tps > rule.getMatchedRule(api).getMaxTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "TPS超过阈值发送紧急预警");
        }
    }
}

修改Alter

java 复制代码
/**
 * @description:告警功能实现
 * @version: 1.0
 * @author:blackcat
 */
public class Alert {


    private List<AlertHandler> alertHandlers = new ArrayList<>();

    public void addAlertHandler(AlertHandler alertHandler) {
        this.alertHandlers.add(alertHandler);
    }

    public void check(ApiInfo apiInfo) {
        // 遍历各种告警处理器
        for (AlertHandler handler : alertHandlers) {
            handler.check(apiInfo);
        }
    }

}

上层组装调用

java 复制代码
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));
    }

    // 饿汉式单例
    private static final ApplicationContext instance = new ApplicationContext();

    private ApplicationContext() {
        instance.initializeBeans();
    }

    public static ApplicationContext getInstance() {
        return instance;
    }


}

新需求改造

1.引入添加新的处理器类TimeoutAlertHandler

java 复制代码
/**
 * @description: 接口请求出错数的告警处理器
 * @version: 1.0
 * @author:blackcat
 */
public class TimeoutAlertHandler extends AlertHandler {

    public TimeoutAlertHandler(AlertRule rule, Notification notification) {
        super(rule, notification);
    }

    @Override
    public void check(ApiInfo apiInfo) {
        String api = apiInfo.getApi();
        long durationOfSeconds = apiInfo.getDurationOfSeconds();
        //新增 当每秒钟接口超时请求个数超过阈值发送紧急预警"
        long timeoutCount = apiInfo.getTimeoutCount();
        long timeoutTps = timeoutCount / durationOfSeconds;
        if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "当每秒钟接口超时请求个数超过阈值发送紧急预警");
        }
    }
}
  1. ApplicationContext新建TimeoutAlertHandler
java 复制代码
```java
public class ApplicationContext {

  

    public void initializeBeans() {
        alertRule = new AlertRule();
        notification = new Notification();
        alert = new Alert();
        // 添加告警处理器
        alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));
        alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));
         alert.addAlertHandler(new TimeoutAlertHandler(alertRule, notification));
    }
 }

3.ApiInfo 属性timeoutCount 和Rule的属性和初始化

总结

可能你会纠结我也明明修改代码了,怎么就是对修改关闭了呢?

第一个修改的地方是向 ApiInfo 类中添加新的属性 timeoutCount。实际上,开闭原则可以应用在不同粒度的代码中,可以是模块,也可以类,还可以是方法(及其属性)。同样一个代码改动,在粗代码粒度下,被认定为"修改",在细代码粒度下,又可以被认定为"扩展"。比如这里的添加属性和方法相当于修改类,在类这个层面,这个代码改动可以被认定为"修改";但这个代码改动并没有修改已有的属性和方法,在方法(及其属性)这一层面,它又可以被认定为"扩展"。

另外一个修改的地方是在 ApplicationContext 类的 initializeBeans() 方法中,往 alert 对象中注册新的 timeoutAlertHandler;在使用 Alert 类的时候,需要给check() 函数的入参 apiInfo 对象设置 timeoutCount 的值。首先说明添加一个新功能,不可能任何模块、类、方法的代码都不"修改",这个是不可能的。主要看修改的是什么内容,这里的修改是上层的代码,而非核心下层的代码,所以是可以接受的。

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
2401_857610034 小时前
多维视角下的知识管理:Spring Boot应用
java·spring boot·后端
代码小鑫4 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计
颜淡慕潇6 小时前
【K8S问题系列 | 9】如何监控集群CPU使用率并设置告警?
后端·云原生·容器·kubernetes·问题解决
独泪了无痕6 小时前
WebStorm 如何调试 Vue 项目
后端·webstorm
怒放吧德德7 小时前
JUC从实战到源码:JMM总得认识一下吧
java·jvm·后端
代码小鑫8 小时前
A025-基于SpringBoot的售楼管理系统的设计与实现
java·开发语言·spring boot·后端·毕业设计
前端SkyRain8 小时前
后端SpringBoot学习项目-项目基础搭建
spring boot·后端·学习
梦想画家8 小时前
理解Rust 生命周期、所有权和借用机制
开发语言·后端·rust
编程乐趣8 小时前
推荐一个.NetCore开源的CMS项目,功能强大、扩展性强、支持插件的系统!
后端