设计模式之美学习-开闭原则

Posted lqblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之美学习-开闭原则相关的知识,希望对你有一定的参考价值。

什么是开闭原则

对修改封闭,对扩展开放

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

违反开闭原则的例子

下面有个监控的例子 当发生错误或者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) {
    //...    
  }
}

 

以上是关于设计模式之美学习-开闭原则的主要内容,如果未能解决你的问题,请参考以下文章

6大设计原则之开闭原则

设计模式七大原则之开闭原则学习

学习设计模式 - 六大基本原则之开闭原则

设计模式之美(c++)-笔记-56-观察者模式

设计模式之美(c++)-笔记-56-观察者模式

设计模式学习笔记“开闭原则”