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

小菜鸟 1年前 (2023-11-03) 阅读数 957 #编程日记
文章标签 后端

定义

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

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

模拟案例

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

文章源地址:https://juejin.cn/post/7297052677673025574
热门
标签列表