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

什么是开闭原则?

开闭原则的英文是Open Closed Principle,简称OCP,它的英文描述是:

Software entities (modules, classes, functions, etc.) should be open for extension, but closed for modification.

软件实体(模块、类、方法等)应该『对扩展开放、对修改关闭』。

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

举个栗子:

下面是一个API接口监控警告的代码,AlertRule存储警告规则,Notification是告警通知类。业务逻辑主要集中在check()函数。

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;
    if (tps > rule.getMatchedRule(api).getMaxTps()) {
      notification.notify(NotificationEmergencyLevel.URGENCY, "...");
    }
    if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
      notification.notify(NotificationEmergencyLevel.SEVERE, "...");
    }
  }
}

现在,如果我要添加一个功能:当每秒接口超时请求个数,超过某个预先设置的最大阈值时,就报警。 我们可以直接在check()函数里修改入参,添加timeoutCount,然后在函数内部添加if分支。

但如果后面需要添加的规则越来越多,就多导致函数的参数很多,管理其他很麻烦。

我们可以重构一下:

  • check()函数的多个入参封装为ApiStatInfo类

  • 引入handler的概念,将if逻辑分散在各个handler

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);
}

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, "...");
    }
  }
}

重构之后,可以通过ApplicationContext单例类,对Alert进行创建和组装。

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);
}

开闭原则有什么好处?

开闭原则实际上是对基于接口或抽象实现『封闭』,基于实现接口或继承实现『开放』的阐释。对扩展开发是为了应为需求变化,对修改关闭时为了保证已有代码的稳定性,最终让系统更具有弹性。

开闭原则并不是免费的,有些情况下,代码的扩展性与可读性是互相冲突的。在某些场景下,代码的扩展性很重要,我们就可以适当牺牲一些代码可读性;在另一些场景下,代码的可读性更重要,我们就需要牺牲一定的代码可扩展性。

如何践行开闭原则?

  • 时刻具备扩展意识、抽象意识、封装意识

  • 往前多思考、预先留好扩展点

  • 识别可变和不可变部分,将可变部分封装,隔离变化,提供抽象的不可变接口

  • 许多设计原则、设计思想、设计模式都是为提高代码扩展性而生的

回顾一下

开闭原则的英文是Open Closed Principle,简称OCP。在添加一个新的功能时,应该是在已有的代码基础上扩展代码(如新增模块、类、方法等),而非修改已有的代码。我们要时刻具备扩展意识、抽象意识、封装意识,识别可变和不可变部分,将可变部分封装,隔离变化,提供抽象的不可变接口。

猜你喜欢

转载自www.cnblogs.com/CocoML/p/12817449.html
今日推荐