什么是开闭原则
对修改封闭,对扩展开放
当我们增加一个功能的时候,应该在已有功能上扩展,而不是在已有功能上进行修改(修改模块、类、方法)
违反开闭原则的例子
下面有个监控的例子 当发生错误或者qps到达某个阀值的时候发出预警
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; //tps是否超过阀值 if (tps > rule.getMatchedRule(api).getMaxTps()) { //触发预警 notification.notify(NotificationEmergencyLevel.URGENCY, "..."); } //错误数超过阀值 if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) { //触发预警 notification.notify(NotificationEmergencyLevel.SEVERE, "..."); } } }
如果需求需要增加请求超过时间多少的阀值
public class Alert { /** * 存储告警规则 可以自行设置 */ private AlertRule rule; //通知类 private Notification notification; public Alert(AlertRule rule, Notification notification) { this.rule = rule; this.notification = notification; } //改动1增加timeOutCount参数 public void check(String api, long requestCount, long errorCount,long timeOutCount long durationOfSeconds) { long tps = requestCount / durationOfSeconds; //tps是否超过阀值 if (tps > rule.getMatchedRule(api).getMaxTps()) { //触发预警 notification.notify(NotificationEmergencyLevel.URGENCY, "..."); } //错误数超过阀值 if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) { //触发预警 notification.notify(NotificationEmergencyLevel.SEVERE, "..."); } // 改动二:添加接口超时处理逻辑 long timeoutTps = timeoutCount / durationOfSeconds; if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) { notification.notify(NotificationEmergencyLevel.URGENCY, "..."); } } }
参数更改了 会涉及到我们调用方的更改 以及原有check方法的修改
基于开闭原则的设计
1.第一步将参数通过类封装起来
2.第二步将if通过Handle封装起来
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); } /** * 进行tps校验通知的预警 */ 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, "..."); } } }
前面讲到的 对外统一暴露的使用类的封装
public class ApplicationContext { private AlertRule alertRule; private Notification notification; private Alert 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)); } public Alert getAlert() { return alert; } // 饿汉式单例 private static final ApplicationContext instance = new ApplicationContext(); private ApplicationContext() { instance.initializeBeans(); } public static ApplicationContext getInstance() { return instance; } } public class Demo { public static void main(String[] args) { ApiStatInfo apiStatInfo = new ApiStatInfo(); // ...省略设置apiStatInfo数据值的代码 ApplicationContext.getInstance().getAlert().check(apiStatInfo); } }
当我们需要进行修改增加超时时间通知的时候
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); }
可能会疑惑上面几个改动点 是否违反了对修改封闭的原则
其实上面只是在原有功能上进行扩展 并没有影响原有逻辑
改动一和改动三 虽然是对实实在在的修改 按时 开闭原则并不是绝对。虽然是修改 但是为了注入新的扩展点,有的修改是不能避免的
如何做到“对扩展开放、修改关闭”
我们要时刻具备扩展意识、抽象意识、封装意识。在写代码的时候,我们要多花点时间思考一下,这段代码未来可能有哪些需求变更,如何设计代码结构,事先留好扩展点,以便在未来需求变更的时候,在不改动代码整体结构、做到最小代码改动的情况下,将新的代码灵活地插入到扩展点上。很多设计原则、设计思想、设计模式,都是以提高代码的扩展性为最终目的的。特别是 23 种经典设计模式,大部分都是为了解决代码的扩展性问题而总结出来的,都是以开闭原则为指导原则的。最常用来提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)。
比如我们要通过kafa进行发送消息
我们通过接口抽象 后期需要改为rocketMq只需要增加对应的实现类
// 这一部分体现了抽象意识 public interface MessageQueue { //... } public class KafkaMessageQueue implements MessageQueue { //... } public class RocketMQMessageQueue implements MessageQueue {//...} public interface MessageFormatter { //... } public class JsonMessageFormatter implements MessageFormatter {//...} public class MessageFormatter implements MessageFormatter {//...} public class Demo { private MessageQueue msgQueue; // 基于接口而非实现编程 public Demo(MessageQueue msgQueue) { // 依赖注入 this.msgQueue = msgQueue; } // msgFormatter:多态、依赖注入 public void sendNotification(Notification notification, MessageFormatter msgFormatter) { //... } }