• 注册
  • 后端开发博客 后端开发博客 关注:0 内容:2359

    如何修改代码又不违背开闭原则呢?看这里

  • 查看作者
  • 打赏作者
  • 当前位置: 职业司 > 后端开发 > 后端开发博客 > 正文
    • 后端开发博客
    • 前言

      看了前一篇重构之后的代码,你可能还会有疑问:

      在添加新的告警逻辑时,改动二(添加新的 handler 类)是基于扩展而非修改的方式来完成的,但改动一、三、四貌似不是基于扩展而是基于修改的方式来完成的,

      那改动一、三、四不就违背了开闭原则吗?

      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);
      }
      复制代码

      我们先来分析一下改动一:往 ApiStatInfo 类中添加新的属性 timeoutCount

      1. 我们不仅往 ApiStatInfo 类中添加了属性,还添加了对应的 getter/setter 方法。那这个问题就转化为:给类中添加新的属性和方法,算作 “修改” 还是 “扩展”?

      我们再一块回忆一下开闭原则的定义:软件实体(模块、类、方法等)应该 “对扩展开放、对修改关闭”。

      从定义中,我们可以看出,开闭原则可以应用在不同粒度的代码中,可以是模块,也可以是类,还可以是方法(及其属性)。

      同样一个代码改动,在粗代码粒度下,被认定为 “修改”,在细代码粒度下,又可以被认定为 “扩展”。

      1. 比如,改动一,添加属性和方法相当于修改类,在类这个层面,这个代码改动可以被认定为 “修改”;但这个代码改动并没有修改已有的属性和方法,在方法(及其属性)这一层面,它又可以被认定为 “扩展”。

      我们回到这条原则的设计初衷:只要它没有破坏原有的代码的正常运行,没有破坏原有的单元测试,我们就可以说,这是一个合格的代码改动

      接下来再来分析一下改动三改动四:在 ApplicationContext 类的 initializeBeans () 方法中,往 alert 对象中注册新的 timeoutAlertHandler;在使用 Alert 类的时候,需要给 check () 函数的入参 apiStatInfo 对象设置 timeoutCount 的值。

      1. 这两处改动都是在方法内部进行的,不管从哪个层面(模块、类、方法)来讲,都不能算是 “扩展”,而是地地道道的 “修改”。

      2. 不过,有些修改是在所难免的,是可以被接受的。

      在重构之后的 Alert 代码中,我们的核心逻辑集中在 Alert 类及其各个 handler 中,当我们在添加新的告警逻辑的时候,Alert 类完全不需要修改,而只需要扩展一个新 handler 类。如果我们把 Alert 类及各个 handler 类合起来看作一个 “模块”,那模块本身在添加新的功能的时候,完全满足开闭原则。

      而且,我们要认识到,添加一个新功能,不可能任何模块、类、方法的代码都不 “修改”,这个是做不到的。类需要创建、组装、并且做一些初始化操作,才能构建成可运行的的程序,这部分代码的修改是在所难免的。我们要做的是尽量让修改操作更集中、更少、更上层,尽量让最核心、最复杂的那部分逻辑代码满足开闭原则。

      更多java原创阅读:javawu.com

      请登录之后再进行评论

      登录

      手机阅读天地(APP)

      • 微信公众号
      • 微信小程序
      • 安卓APP
      手机浏览,惊喜多多
      匿名树洞,说我想说!
      问答悬赏,VIP可见!
      密码可见,回复可见!
      即时聊天、群聊互动!
      宠物孵化,赠送礼物!
      动态像框,专属头衔!
      挑战/抽奖,金币送不停!
      赶紧体会下,不会让你失望!
    • 实时动态
    • 签到
    • 做任务
    • 发表内容
    • 偏好设置
    • 到底部
    • 帖子间隔 侧栏位置:
    • 还没有账号?点这里立即注册