共计 4378 个字符,预计需要花费 11 分钟才能阅读完成。
前言
上一篇 开闭准则 最有用的代码改变是基于“批改”的形式来实现新性能的。如果咱们遵循开闭准则,也就是“对扩大凋谢、对批改敞开”。那如何通过“扩大”的形式,来实现同样的性能呢?
重构
咱们先重构一下之前的 Alert 代码,让它的扩展性更好一些。重构的内容次要蕴含两局部:
- 第一局部是将 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, "...");
}
}
}
下面的代码是对 Alert 的重构,咱们再来看下,重构之后的 Alert 该如何应用呢?具体的应用代码我也写在这里了。
其中,ApplicationContext 是一个单例类,负责 Alert 的创立、组装(alertRule 和 notification 的依赖注入)、初始化(增加 handlers)工作。
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));
}
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);
}
}
当初,咱们再来看下,基于重构之后的代码,如果再增加下面讲到的那个新性能,每秒钟接口超时申请个数超过某个最大阈值就告警,咱们又该如何改变代码呢?次要的改变有上面到处。
- 第一处改变是:在 ApiStatInfo 类中增加新的属性 timeoutCount。
- 第二处改变是:增加新的 TimeoutAlertHander 类。
- 第三处改变是:在 ApplicationContext 类的 initializeBeans () 办法中,往 alert 对象中注册新的 timeoutAlertHandler。
- 第四处改变是:在应用 Alert 类的时候,须要给 check () 函数的入参 apiStatInfo 对象设置 timeoutCount 的值。
改变之后的代码如下所示:
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);
}
重构之后的代码更加灵便和易扩大。如果咱们要想增加新的告警逻辑,只须要基于扩大的形式创立新的 handler 类即可,不须要改变原来的 check () 函数的逻辑。而且,咱们只须要为新的 handler 类增加单元测试,老的单元测试都不会失败,也不必批改。
重点回顾
如何了解“对扩大凋谢、对批改敞开”?
增加一个新的性能,应该是通过在已有代码根底上扩大代码(新增模块、类、办法、属性等),而非批改已有代码(批改模块、类、办法、属性等)的形式来实现。对于定义,咱们有两点要留神。第一点是,开闭准则并不是说齐全杜绝批改,而是以最小的批改代码的代价来实现新性能的开发。第二点是,同样的代码改变,在粗代码粒度下,可能被认定为“批改”;在细代码粒度下,可能又被认定为“扩大”。
更多 java 原创浏览:https://javawu.com