当前位置:

首页 > 编程开发 > Java重构技巧:用Predicate简化单行重复代码

Java重构技巧:用Predicate简化单行重复代码

本文旨在探讨Java中一种常见的代码重复问题:当多个方法主体结构相似,仅在某一特定逻辑(如过滤条件)上存在细微差异时。我们将通过引入Java8的函数式接口Predicate,演示如何将这些差异抽象化,从而重构出简洁、可维护且易于扩展的通用方法,有效避免代码冗余。

本文目录

    Java代码重构:利用Predicate消除方法中单行差异的重复代码

    本文旨在探讨Java中一种常见的代码重复问题:当多个方法主体结构相似,仅在某一特定逻辑(如过滤条件)上存在细微差异时。我们将通过引入Java 8的函数式接口Predicate,演示如何将这些差异抽象化,从而重构出简洁、可维护且易于扩展的通用方法,有效避免代码冗余。

    在软件开发中,我们经常会遇到这样的场景:多个方法执行着几乎相同的业务逻辑,唯一的区别在于其中的一小部分,例如一个条件判断、一个数据转换规则或一个特定的操作。这种“几乎相同”的代码模式是代码重复的常见来源,它不仅增加了代码库的体积,更重要的是,它极大地损害了代码的可维护性、可读性和可扩展性。当需要修改或修复bug时,开发者可能需要在多个地方进行相同的改动,这无疑增加了出错的风险和维护成本。

    考虑以下Java代码示例,其中A类包含两个方法findCreated()和findRegistered(),它们都对list进行流式处理,包括一些共同的过滤和映射操作,但最终的filter条件有所不同:

    public class A {
       private List list;
    
       // ... 其他成员和方法
    
       public String findCreated() {
          return list.stream()
                     // .some filter operations
                     .filter(b -> b.isCreated()) // 差异点
                     // .some mapping
                     .collect(Collectors.joining(",")); // 假设最终收集为字符串
       }
    
       public String findRegistered() {
          return list.stream()
                     // .some filter operations
                     .filter(b -> b.isRegistered()) // 差异点
                     // .some mapping
                     .collect(Collectors.joining(",")); // 假设最终收集为字符串
       }
    }
    
    public class B {
        private boolean created;
        private boolean registered;
    
        // ... 构造函数、getter/setter
    
        public boolean isCreated() {
            return created;
        }
    
        public boolean isRegistered() {
            return registered;
        }
    }

    在这个例子中,findCreated()和findRegistered()方法除了.filter(b -> b.isCreated())和.filter(b -> b.isRegistered())这一行之外,其余代码完全相同。这种重复是典型的重构目标。

    解决方案:利用java.util.function.Predicate

    Java 8引入的函数式接口为解决这类问题提供了强大的工具。java.util.function.Predicate是一个函数式接口,它定义了一个抽象方法boolean test(T t),用于表示一个参数的布尔值函数(即一个条件判断)。通过将变化的过滤条件抽象为Predicate,我们可以创建一个通用的私有方法来封装共享的逻辑。

    重构步骤

    1. 识别通用逻辑与差异点:

      • 通用逻辑:list.stream().some filter operations... .some mapping
      • 差异点:.filter(b -> b.isCreated()) 和 .filter(b -> b.isRegistered())
    2. 创建通用私有方法: 定义一个私有方法,例如find(Predicate filter),该方法接受一个Predicate作为参数。将所有共享的流操作放入此方法中,并在需要应用差异化过滤的地方使用传入的filter参数。

    3. 重写公共方法: 修改原有的公共方法(如findCreated()和findRegistered()),使其不再包含重复的逻辑,而是简单地调用新创建的通用私有方法,并传入各自特定的Predicate实现。Java 8的方法引用(Method Reference)在这里可以极大地简化代码。例如,B::isCreated是b -> b.isCreated()的简洁形式。

    示例代码

    经过重构后的A类如下所示:

    import java.util.List;
    import java.util.function.Predicate;
    import java.util.stream.Collectors; // 假设需要收集器
    
    public class A {
       private List list;
    
       // 假设 list 在构造函数或其他地方被初始化
       public A(List list) {
           this.list = list;
       }
    
       /**
        * 通用的查找方法,接受一个Predicate作为过滤条件。
        * 封装了共享的流处理逻辑。
        * @param filter 用于过滤B对象的条件
        * @return 经过过滤和映射后的字符串结果
        */
       private String find(Predicate filter) {
          return list.stream()
                     // 假设这里有一些通用的过滤操作,例如:
                     // .filter(b -> b.getSomeProperty() != null)
                     .filter(filter) // 应用传入的差异化过滤条件
                     // 假设这里有一些通用的映射操作,例如:
                     // .map(b -> b.getName())
                     .map(B::toString) // 假设将B对象转换为其字符串表示
                     .collect(Collectors.joining(",")); // 假设最终收集为逗号分隔的字符串
       }
    
       /**
        * 查找所有已创建的B对象。
        * @return 已创建B对象的字符串列表
        */
       public String findCreated() {
          return find(B::isCreated); // 使用方法引用传入isCreated条件
       }
    
       /**
        * 查找所有已注册的B对象。
        * @return 已注册B对象的字符串列表
        */
       public String findRegistered() {
          return find(B::isRegistered); // 使用方法引用传入isRegistered条件
       }
    }
    
    // B类保持不变
    // public class B { ... }

    收益分析

    通过上述重构,我们获得了以下显著优势:

    • 消除代码重复: 核心的流处理逻辑现在只存在于一个地方,即find私有方法中。
    • 提高可维护性: 如果将来需要修改通用的过滤或映射逻辑,只需在一个地方进行更改,而不是在多个方法中重复操作。
    • 增强可读性: 公共方法findCreated()和findRegistered()变得非常简洁,清晰地表达了它们的目的——调用通用查找方法并提供特定的过滤条件。
    • 更好的扩展性: 如果未来需要添加findDeleted()或findArchived()等方法,只需添加一行代码调用find方法并传入新的Predicate即可,无需复制整个流处理链。

    注意事项与最佳实践

    1. 适用场景: 这种模式最适用于方法间差异集中在单一、可抽象的逻辑点(如条件判断、转换函数)时。如果差异点过多或过于复杂,可能需要考虑其他设计模式(如策略模式)。
    2. 其他函数式接口: 除了Predicate,Java 8还提供了Function(用于转换)、Consumer(用于消费)、Supplier(用于提供)等多种函数式接口,可以根据具体的差异类型选择合适的接口进行抽象。
    3. 方法引用与Lambda表达式: 在本例中,B::isCreated是方法引用,它比b -> b.isCreated()更简洁。当Lambda表达式仅调用一个现有方法时,优先考虑使用方法引用。
    4. 可见性: 通用方法通常应声明为private,以封装内部实现细节,并通过公共方法提供受控的访问接口。

    总结

    利用java.util.function.Predicate等函数式接口是Java 8及更高版本中避免代码重复、编写更简洁和更具表达力代码的强大手段。通过识别方法间的共同模式和差异点,并运用函数式接口将差异点抽象化,我们可以有效地重构代码,使其更加健壮、易于维护和扩展。这不仅是提高代码质量的有效途径,也是现代Java开发中不可或缺的实践。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    GitHub Actions重跑失败作业会使用最新代码吗
    GitHub Actions重跑失败作业会使用最新代码吗

    解释已有运行重跑的提交和权限边界,给出确认、失败作业重跑及结果核对命令,区分漏触发事件。

    chat-demo v0.1.1怎么本地运行并理解DeepSeek历史数据边界
    chat-demo v0.1.1怎么本地运行并理解DeepSeek历史数据边界

    chat-demo v0.1.1可以作为静态聊天客户端在本地运行,但模型、搜索、文件和语音能力仍由DeepSeek API提供。源码运行环境为Node.js 22.12及以上、Git与pnpm。Demo版本号不等于应用API版本;这套应用API未单独标版本,默认聊天模型ID是deepseek-flash。

    GitHub Copilot CLI v1.0.111的用量图与上下文比例应该怎样理解
    GitHub Copilot CLI v1.0.111的用量图与上下文比例应该怎样理解

    理解GitHub Copilot CLI v1.0.111的高级请求百分比、用户提示活动图与实际路由模型窗口,区分显示口径和任务状态。

    n8n 2.47.6、2.47.7、2.48.3 Beta、2.48.4 Beta、3.2.2预发布与1.123.96适用哪些安全修复?
    n8n 2.47.6、2.47.7、2.48.3 Beta、2.48.4 Beta、3.2.2预发布与1.123.96适用哪些安全修复?

    按维护分支、利用权限与节点类型对应四项修复,区分正式补丁、Beta和临时缓解的适用边界。

    GitHub合并故障的已缓解、监控与已解决有什么不同
    GitHub合并故障的已缓解、监控与已解决有什么不同

    GitHub合并故障中的“已缓解并监控”和“已解决”不能互换。前者表示影响已经采取措施缓解,仍在观察稳定性;后者是供应商正式结束这场事故的状态。11月13日的Pull Requests合并超时事件先进入监控,随后于北京时间11月14日02:29标记为已解决。

    llama.cpp v1.0.0的网页聊天分组与Markdown编辑适合哪些场景
    llama.cpp v1.0.0的网页聊天分组与Markdown编辑适合哪些场景

    llama.cpp v1.0.0改善聊天组织、长消息编辑和附件预览;界面变化与底层API迁移、缓存及访问边界需要分别理解。

    Postman 12.36.4 更新后仍报错,怎么判断是哪一层的问题
    Postman 12.36.4 更新后仍报错,怎么判断是哪一层的问题

    Postman 12.36.4 更新后仍有异常时,可按应用、API请求和平台服务区分问题范围,避免把补丁版本当作故障原因。

    n8n 2.47.6、2.48.3 Beta与1.123.96适合哪些部署环境?
    n8n 2.47.6、2.48.3 Beta与1.123.96适合哪些部署环境?

    按正式、预发布和1.x维护渠道对应补丁,再结合Redis、Agent端点和Pinecone索引判断适用范围。

    Codex CLI 0.169.0与0.171.0-alpha.2怎样选择,终端工作区适合哪些任务
    Codex CLI 0.169.0与0.171.0-alpha.2怎样选择,终端工作区适合哪些任务

    Codex CLI 0.169.0提供有明确说明的终端工作区能力;0.171.0-alpha.2为预发布,当前没有功能清单,选用时应分开评估。

    llama.cpp b12345的Hexagon后端支持哪些Q5量化格式?
    llama.cpp b12345的Hexagon后端支持哪些Q5量化格式?

    llama.cpp b12345的预发布构建为Hexagon HTP后端增加Q5_0和Q5_1支持。选择模型时需要核对完整量化名称及实际运行后端,不能只看到“Q5”就视为同一种格式。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    为什么要购买正版软件?
    01 官方授权

    授权来源透明,订单与许可信息可随时查询。

    02 即时交付

    付款成功后自动发放激活信息,支持订单追踪。

    03 中文协助

    安装、激活、迁移与续费问题都有专人响应。

    04 长期可用

    提供升级提醒与授权管理,换机时也能轻松迁移。

    网站备案号:苏ICP备2026018738号-1 联系邮箱:bd@zhengruan.com 网站地图

    Copyright ©2018-2026