• 设计模式之美学习-开闭原则(五)


    什么是开闭原则

    对修改封闭,对扩展开放

    当我们增加一个功能的时候,应该在已有功能上扩展,而不是在已有功能上进行修改(修改模块、类、方法)

    违反开闭原则的例子

    下面有个监控的例子 当发生错误或者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) {
        //...    
      }
    }
  • 相关阅读:
    C语言习题(结构)
    java变量
    大咖分享 | 一文解锁首届云创大会干货——上篇(文末附演讲ppt文件免费下载)
    深入解读Service Mesh的数据面Envoy
    appium封装显示等待Wait类和ExpectedCondition接口
    Jmeter压测Thrift服务接口
    浏览器插件及好用的小工具
    Jmeter入门实例
    BugBash活动分享
    如何作缺陷分析
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12125688.html
Copyright © 2020-2023  润新知