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

定义

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

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

模拟案例

场景: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 的值。首先说明添加一个新功能,不可能任何模块、类、方法的代码都不"修改",这个是不可能的。主要看修改的是什么内容,这里的修改是上层的代码,而非核心下层的代码,所以是可以接受的。

相关推荐
后端码匠39 分钟前
Spring Boot3+Vue2极速整合:10分钟搭建DeepSeek AI对话系统
人工智能·spring boot·后端
FLZJ_KL1 小时前
【设计模式】【创建型模式】单例模式(Singleton)
java·单例模式·设计模式
可乐张1 小时前
AutoGen 技术博客系列 (九):从 v0.2 到 v0.4 的迁移指南
后端·llm
可乐张1 小时前
AutoGen 技术博客系列 (八):深入剖析 Swarm—— 智能体协作的新范式
后端·llm
计算机-秋大田1 小时前
基于Spring Boot的农产品智慧物流系统设计与实现(LW+源码+讲解)
java·开发语言·spring boot·后端·spring·课程设计
计算机毕设指导61 小时前
基于SpringBoot的城乡商城协作系统【附源码】
java·spring boot·后端·mysql·spring·tomcat·maven
华子w9089258591 小时前
基于数据可视化+SpringBoot+安卓端的数字化施工项目计划与管理平台设计和实现
java·spring boot·后端
橘猫云计算机设计1 小时前
基于Django的购物商城平台的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
java·数据库·spring boot·后端·django
2501_903238651 小时前
Spring Boot日志配置与环境切换实战
数据库·spring boot·后端·个人开发
WeiLai11122 小时前
面试基础--微服务架构:如何拆分微服务、数据一致性、服务调用
java·分布式·后端·微服务·中间件·面试·架构