前言
上一篇开闭准则最有用的代码改变是基于 “批改” 的形式来实现新性能的。如果咱们遵循开闭准则,也就是 “对扩大凋谢、对批改敞开”。那如何通过 “扩大” 的形式,来实现同样的性能呢?
重构
咱们先重构一下之前的 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 {// 代码未改变...}// 改变二:增加新的 handlerpublic 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