当前位置: 首页 > news >正文

Fluent Interface 流畅接口

Fluent Interface(流畅接口):让代码说人话的设计艺术

📚 什么是Fluent Interface?

Fluent Interface 是一种面向对象的API设计风格,通过方法的链式调用让代码读起来像自然语言一样流畅。它不是具体的框架或技术,而是一种设计思想

简单说:就是让你的代码从"命令式"变成"声明式"。

🎯 核心理念:让代码更易读

改造前(传统写法):

Query query = new Query();
query.setSelect("*");
query.setFrom("users");
query.setWhere("age > 18");
query.setOrderBy("name");

改造后(Fluent Interface):

Query query = new Query().select("*").from("users").where("age > 18").orderBy("name");

看!是不是更像在说一句话:"查询,选择所有,从用户表,条件是年龄大于18,按名字排序"?

🔧 两种实现方式及完整实现示例

方式1:返回 this(修改自身)- 构建器模式

// SQL查询构建器完整实现
import java.util.ArrayList;
import java.util.List;public class SQLQuery {private String table;private List<String> selects = new ArrayList<>();private List<String> wheres = new ArrayList<>();private List<String> orderBys = new ArrayList<>();private Integer limit;private Integer offset;// 私有构造器private SQLQuery() {}// 创建查询的入口点public static SQLQuery create() {return new SQLQuery();}// FROM 子句public SQLQuery from(String table) {this.table = table;return this;  // ← 返回this实现链式调用}// SELECT 子句public SQLQuery select(String... columns) {for (String column : columns) {this.selects.add(column);}return this;}// WHERE 子句public SQLQuery where(String condition) {this.wheres.add(condition);return this;}// AND 条件public SQLQuery and(String condition) {if (!wheres.isEmpty()) {this.wheres.add("AND " + condition);}return this;}// OR 条件public SQLQuery or(String condition) {if (!wheres.isEmpty()) {this.wheres.add("OR " + condition);}return this;}// ORDER BY 子句public SQLQuery orderBy(String column) {this.orderBys.add(column);return this;}// 降序排序public SQLQuery orderByDesc(String column) {this.orderBys.add(column + " DESC");return this;}// LIMIT 子句public SQLQuery limit(int limit) {this.limit = limit;return this;}// OFFSET 子句public SQLQuery offset(int offset) {this.offset = offset;return this;}// 分组条件public SQLQuery groupBy(String... columns) {// 实现略...return this;}// 构建SQL字符串public String build() {if (table == null) {throw new IllegalStateException("必须指定FROM子句");}StringBuilder sql = new StringBuilder();// SELECT子句if (selects.isEmpty()) {sql.append("SELECT *");} else {sql.append("SELECT ").append(String.join(", ", selects));}// FROM子句sql.append(" FROM ").append(table);// WHERE子句if (!wheres.isEmpty()) {sql.append(" WHERE ");// 第一个条件不加AND/OR前缀sql.append(wheres.get(0));// 后续条件已有前缀for (int i = 1; i < wheres.size(); i++) {sql.append(" ").append(wheres.get(i));}}// ORDER BY子句if (!orderBys.isEmpty()) {sql.append(" ORDER BY ").append(String.join(", ", orderBys));}// LIMIT子句if (limit != null) {sql.append(" LIMIT ").append(limit);}// OFFSET子句if (offset != null) {sql.append(" OFFSET ").append(offset);}return sql.toString();}
}// 使用示例
public class SQLExample {public static void main(String[] args) {// 构建复杂的SQL查询String sql = SQLQuery.create().from("users").select("id", "name", "email", "age").where("age >= 18").and("status = 'ACTIVE'").or("is_vip = true").orderBy("created_at").orderByDesc("last_login").limit(20).offset(10).build();System.out.println("生成的SQL:");System.out.println(sql);// 输出结果:// SELECT id, name, email, age // FROM users // WHERE age >= 18 AND status = 'ACTIVE' OR is_vip = true // ORDER BY created_at, last_login DESC // LIMIT 20 OFFSET 10}
}

方式2:返回 new 对象(不可变)- 值对象

// 金额计算完整实现
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Currency;public class Money {private final BigDecimal amount;private final Currency currency;// 私有构造器private Money(BigDecimal amount, Currency currency) {this.amount = amount.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_EVEN);this.currency = currency;}// 工厂方法public static Money of(BigDecimal amount, Currency currency) {return new Money(amount, currency);}// 加法 - 返回新对象public Money add(Money other) {checkSameCurrency(other);return new Money(  // ← 返回新对象this.amount.add(other.amount),this.currency);}// 乘法 - 返回新对象public Money multiply(BigDecimal factor) {return new Money(this.amount.multiply(factor),this.currency);}// 减法 - 返回新对象public Money subtract(Money other) {checkSameCurrency(other);return new Money(this.amount.subtract(other.amount),this.currency);}// 百分比计算 - 返回新对象public Money percentage(BigDecimal percent) {return multiply(percent.divide(BigDecimal.valueOf(100)));}private void checkSameCurrency(Money other) {if (!this.currency.equals(other.currency)) {throw new IllegalArgumentException("币种不匹配");}}@Overridepublic String toString() {return String.format("%s %.2f", currency.getSymbol(), amount);}
}// 使用示例
public class MoneyExample {public static void main(String[] args) {Currency usd = Currency.getInstance("USD");Money salary = Money.of(new BigDecimal("5000"), usd);Money bonus = Money.of(new BigDecimal("1000"), usd);// 链式计算Money total = salary.add(bonus)                     // 新对象: $6000.multiply(new BigDecimal("1.1")) // 新对象: $6600.percentage(new BigDecimal("15")); // 新对象: $990 (6600的15%)System.out.println("月薪: " + salary);     // $5000.00System.out.println("奖金: " + bonus);      // $1000.00System.out.println("总收入: " + total);    // $990.00// 验证原对象不变System.out.println("月薪仍为: " + salary); // 仍为 $5000.00}
}

📊 两种实现方式对比

特性 返回 this(可变) 返回 new 对象(不可变)
内存使用 只创建一个对象 每个操作创建新对象
性能 高(无对象创建) 相对较低
线程安全 不安全 安全
调试难度 状态变化难追踪 对象状态清晰
典型应用 Builder模式、配置 值对象、数学计算
关键代码 return this; return new Class(...);

🏆 何时使用哪种?

选择返回 this 的情况:

  1. 构建复杂对象:Builder模式(如SQL查询构建器)
  2. 配置对象:一次构建,多次使用
  3. 性能敏感场景:避免对象创建开销
  4. 中间状态不重要:只关心最终结果

选择返回 new 对象 的情况:

  1. 值对象:金额、坐标、日期等
  2. 数学/物理运算:向量、矩阵运算
  3. 需要线程安全:多线程环境
  4. 函数式编程:符合无副作用原则

🚀 更多实际应用示例

1. HTTP客户端配置

HttpClient client = new HttpClient().url("https://api.example.com").timeout(5000).retry(3).header("Content-Type", "application/json").header("Authorization", "Bearer token");

2. 测试断言框架

assertThat(user).hasName("张三").isAdult().hasValidEmail().isActive().hasRole("ADMIN");

3. 文件处理流水线

FileProcessor.process("input.txt").readLines().filter(line -> !line.startsWith("#")).map(String::trim).writeTo("output.txt").compress().uploadTo("s3://bucket/output.zip");

📝 设计技巧

  1. 方法名要像动词select()from()where()
  2. 避免set/get前缀:直接用名词或动词
  3. 保持连贯性:每个方法都能自然衔接下一个
  4. 考虑可读性:读代码应该像读句子
  5. 适时终止链:需要明确的终点如build()collect()

⚠️ 注意事项

  1. 不要过度使用:简单对象不需要Fluent Interface
  2. 保持一致性:要么全链式,要么全传统
  3. 考虑IDE支持:链式调用时IDE提示可能不够明确
  4. 调试可能稍难:错误发生在长链中的某一步

🌟 核心价值

Fluent Interface 不是为了让代码更"酷",而是为了让代码更清晰。 好的 API 设计应该让使用者不用查文档就能猜到用法。

http://www.jsqmd.com/news/370510/

相关文章:

  • 吐血推荐!降AIGC软件 千笔·专业降AIGC智能体 VS PaperRed,专科生专属神器!
  • 高温胶带怎么选?耐温、持粘力与残胶控制,一文读懂TOP5品牌优劣 - 深度智识库
  • 2026年洗地机产品推荐:基于长期使用与维护成本评价,解决操作复杂与异味痛点 - 品牌推荐
  • 2026年抗过敏猫粮产品推荐:五大品牌综合评测与选型指南 - 品牌推荐
  • 家用油烟分离油烟机哪个品牌好 - 品牌企业推荐师(官方)
  • 如何将联系人从 iPhone 转移到Android
  • arthas入门参考
  • 2026年度抗过敏猫粮产品推荐榜单:成分安全与喂养效果双维度综合评估 - 品牌推荐
  • 基于DPDK的高性能网络方案 - 指南
  • 云上Openclaw(Clawdbot)快速接入腾讯云智能体开发平台ADP指南
  • 实用指南:Redis底层原理-持久化【详细易懂】
  • 2026年抗过敏猫粮产品推荐:基于成分功效与喂养实证维度下的权威榜单 - 品牌推荐
  • 2026年洗地机产品终极评测(权威数据与市场趋势双重背书)| 家庭选购避坑全指南 - 品牌推荐
  • 2026年儿童牙膏品牌推荐:基于2026年成分安全趋势评测,涵盖幼儿与学龄儿童核心场景 - 品牌推荐
  • jvm相关概念
  • 期货套保系统指标监控面板设计与应用
  • Mysql 5.7 windows安装 zip安装
  • 期现对冲交易系统合同状态监控功能解析
  • 利用LuatOS的errDump功能实现运行时错误追踪与上报
  • 洗地机哪个品牌好?2026年洗地机产品推荐与排名,解决维护与性价比核心痛点 - 品牌推荐
  • 铝箔胶带怎么选?基材纯度、耐候性与定制化服务Top5厂家全攻略 - 深度智识库
  • OpenClaw从本地运行时到云端推理的完全接管机制与安全边界
  • 2026银川酒店民宿装修推荐哪家?专业工装 实力品牌 省心落地指南 - 宁夏壹山网络
  • 2026年度儿童牙膏品牌TOP5综合评估与选型指南 - 品牌推荐
  • 2026年2月,为你推荐评价好的资质代办公司,代办营业执照/注册公司/资质代办/代办公司/公司注册,代办公司推荐 - 品牌推荐师
  • 杭州GEO优化推荐:2026年提升搜索可见度的优选,GEO优化/GEO优化服务,GEO优化实力厂家找哪家 - 品牌推荐师
  • 2026年度儿童牙膏品牌推荐榜单:成分安全与防蛀功效双维度综合评估 - 品牌推荐
  • 2026年儿童牙膏品牌推荐:基于分龄护理趋势评价,针对龋齿与成分焦虑提供选购推荐 - 品牌推荐
  • 告别论文恐惧症!实测6款免费AI写论文工具,写作无压力,初稿轻松搞定! - 麟书学长
  • a16z 领投 AI 伴侣 Shizuku AI:让社区参与 AI 角色成长;夸克 AI 眼镜新增直播功能丨日报