前言
看了前一篇重构之后的代码,你可能还会有疑问:
在添加新的告警逻辑时,改动二(添加新的 handler 类)是基于扩展而非修改的方式来完成的,但改动一、三、四貌似不是基于扩展而是基于修改的方式来完成的,那改动一、三、四不就违背了开闭原则吗?
publicclassAlert{// 代码未改动... }publicclassApiStatInfo{// 省略 constructor/getter/setter 方法privateString api;privatelongrequestCount;privatelongerrorCount;privatelongdurationOfSeconds;privatelongtimeoutCount;// 改动一:添加新字段}publicabstractclassAlertHandler{// 代码未改动... }publicclassTpsAlertHandlerextendsAlertHandler{// 代码未改动...}publicclassErrorAlertHandlerextendsAlertHandler{// 代码未改动...}// 改动二:添加新的 handlerpublicclassTimeoutAlertHandlerextendsAlertHandler{// 省略代码...}publicclassApplicationContext{privateAlertRule alertRule;privateNotification notification;privateAlert alert;publicvoidinitializeBeans(){
alertRule =newAlertRule(/*. 省略参数.*/);// 省略一些初始化代码notification =newNotification(/*. 省略参数.*/);// 省略一些初始化代码alert =newAlert();
alert.addAlertHandler(newTpsAlertHandler(alertRule, notification));
alert.addAlertHandler(newErrorAlertHandler(alertRule, notification));// 改动三:注册 handleralert.addAlertHandler(newTimeoutAlertHandler(alertRule, notification));
}//... 省略其他未改动代码...}publicclassDemo{publicstaticvoidmain(String[] args){
ApiStatInfo apiStatInfo =newApiStatInfo();//... 省略 apiStatInfo 的 set 字段代码apiStatInfo.setTimeoutCount(289);// 改动四:设置 tiemoutCount 值ApplicationContext.getInstance().getAlert().check(apiStatInfo);
}
我们先来分析一下改动一:往ApiStatInfo类中添加新的属性timeoutCount。
- 我们不仅往ApiStatInfo类中添加了属性,还添加了对应的getter/setter方法。那这个问题就转化为:给类中添加新的属性和方法,算作 修改 还是 扩展?
我们再一块回忆一下开闭原则的定义:软件实体(模块、类、方法等)应该 对扩展开放、对修改关闭。从定义中,我们可以看出,开闭原则可以应用在不同粒度的代码中,可以是模块,也可以是类,还可以是方法(及其属性)。同样一个代码改动,在粗代码粒度下,被认定为 修改,在细代码粒度下,又可以被认定为 扩展。
- 比如,改动一,添加属性和方法相当于修改类,在类这个层面,这个代码改动可以被认定为 修改;但这个代码改动并没有修改已有的属性和方法,在方法(及其属性)这一层面,它又可以被认定为 扩展。
我们回到这条原则的设计初衷:只要它没有破坏原有的代码的正常运行,没有破坏原有的单元测试,我们就可以说,这是一个合格的代码改动。
接下来再来分析一下改动三和改动四:在ApplicationContext类的initializeBeans ()方法中,往alert对象中注册新的timeoutAlertHandler;在使用Alert类的时候,需要给check ()函数的入参apiStatInfo对象设置timeoutCount的值。
- 这两处改动都是在方法内部进行的,不管从哪个层面(模块、类、方法)来讲,都不能算是 扩展,而是地地道道的 修改。
- 在重构之后的 Alert 代码中,我们的核心逻辑集中在 Alert 类及其各个 handler 中,当我们在添加新的告警逻辑的时候,Alert 类完全不需要修改,而只需要扩展一个新 handler 类。如果我们把 Alert 类及各个 handler 类合起来看作一个 模块,那模块本身在添加新的功能的时候,完全满足开闭原则。
而且,我们要认识到,添加一个新功能,不可能任何模块、类、方法的代码都不 修改,这个是做不到的。类需要创建、组装、并且做一些初始化操作,才能构建成可运行的的程序,这部分代码的修改是在所难免的。我们要做的是尽量让修改操作更集中、更少、更上层,尽量让最核心、最复杂的那部分逻辑代码满足开闭原则。
更多java原创阅读:https://javawu.com