Step Builder模式实战指南:构建类型安全的流式API
一、什么是Step Builder模式
1.1 传统Builder模式的局限性
在Java开发中,我们经常使用Builder模式来构建复杂对象。传统的Builder模式虽然解决了构造函数参数过多的问题,但存在一个明显的缺陷:无法在编译期保证必填参数的完整性。
传统Builder的问题示例:
java
// 传统Builder模式
public class User {
private String username; // 必填
private String password; // 必填
private String email; // 必填
private String phone; // 可选
private Integer age; // 可选
public static class Builder {
private String username;
private String password;
private String email;
private String phone;
private Integer age;
public Builder username(String username) {
this.username = username;
return this;
}
public Builder password(String password) {
this.password = password;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder phone(String phone) {
this.phone = phone;
return this;
}
public Builder age(Integer age) {
this.age = age;
return this;
}
public User build() {
// 运行时检查,容易遗漏
if (username == null || password == null || email == null) {
throw new IllegalStateException("缺少必填字段");
}
return new User(this);
}
}
}
// 使用时可能会忘记设置必填字段
User user = new User.Builder()
.username("john")
// 忘记设置password和email
.phone("123456")
.build(); // 运行时才会抛出异常!
1.2 Step Builder模式的优势
Step Builder模式(也称为Staged Builder或Telescopic Builder)通过类型系统在编译期强制要求按顺序设置必填参数,从而避免了运行时错误。
核心思想:
- 每个必填参数对应一个构建步骤(Step)
- 每个步骤只暴露下一步需要的方法
- 利用接口和泛型确保类型安全
- 编译器强制完成所有必填步骤
Step Builder的调用流程:
Step 1: Username Step 2: Password Step 3: Email Step 4: Build
↓ ↓ ↓ ↓
[username()] ──→ [password()] ──→ [email()] ──→ [build()]
[phone()]
[age()]
二、Step Builder基础实现
2.1 简单示例:用户注册
java
package com.example.builder;
/**
* Step Builder模式 - 用户注册示例
*/
public class User {
private final String username;
private final String password;
private final String email;
private final String phone;
private final Integer age;
private User(Builder builder) {
this.username = builder.username;
this.password = builder.password;
this.email = builder.email;
this.phone = builder.phone;
this.age = builder.age;
}
// ============ Step Builder接口定义 ============
/**
* 步骤1: 设置用户名
*/
public interface UsernameStep {
PasswordStep username(String username);
}
/**
* 步骤2: 设置密码
*/
public interface PasswordStep {
EmailStep password(String password);
}
/**
* 步骤3: 设置邮箱
*/
public interface EmailStep {
BuildStep email(String email);
}
/**
* 步骤4: 构建对象(可选参数)
*/
public interface BuildStep {
BuildStep phone(String phone);
BuildStep age(Integer age);
User build();
}
// ============ Builder实现类 ============
private static class Builder implements UsernameStep, PasswordStep, EmailStep, BuildStep {
private String username;
private String password;
private String email;
private String phone;
private Integer age;
@Override
public PasswordStep username(String username) {
this.username = username;
return this;
}
@Override
public EmailStep password(String password) {
this.password = password;
return this;
}
@Override
public BuildStep email(String email) {
this.email = email;
return this;
}
@Override
public BuildStep phone(String phone) {
this.phone = phone;
return this;
}
@Override
public BuildStep age(Integer age) {
this.age = age;
return this;
}
@Override
public User build() {
return new User(this);
}
}
// ============ 静态工厂方法 ============
public static UsernameStep builder() {
return new Builder();
}
// Getters
public String getUsername() { return username; }
public String getPassword() { return password; }
public String getEmail() { return email; }
public String getPhone() { return phone; }
public Integer getAge() { return age; }
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
", age=" + age +
'}';
}
}
使用示例:
java
public class StepBuilderDemo {
public static void main(String[] args) {
// ✅ 正确使用 - 编译通过
User user1 = User.builder()
.username("john_doe")
.password("securePass123")
.email("john@example.com")
.phone("1234567890")
.age(25)
.build();
System.out.println(user1);
// ✅ 只设置必填字段
User user2 = User.builder()
.username("jane_doe")
.password("password456")
.email("jane@example.com")
.build();
System.out.println(user2);
// ❌ 编译错误 - 缺少必填字段
// User user3 = User.builder()
// .username("alice")
// .build(); // 编译失败:找不到build()方法
}
}
2.2 工作原理分析
类型系统保证调用顺序:
User.builder() 返回类型: UsernameStep
↓ 只能调用username()
.username("john") 返回类型: PasswordStep
↓ 只能调用password()
.password("pass123") 返回类型: EmailStep
↓ 只能调用email()
.email("john@example.com") 返回类型: BuildStep
↓ 可以调用phone()、age()或build()
.phone("123456") 返回类型: BuildStep
↓ 可以调用age()或build()
.build() 返回类型: User
类型安全的关键:
- 接口链:每个步骤返回不同的接口类型
- 单一实现:Builder类实现所有接口
- 方法可见性:每个接口只暴露特定方法
- 编译期检查:IDE和编译器强制按顺序调用
三、实战案例:HTTP请求构建器
3.1 场景分析
在构建HTTP客户端时,某些参数是必需的(如URL、HTTP方法),而某些是可选的(如请求头、超时时间)。使用Step Builder可以确保必填参数不会遗漏。
3.2 完整实现
java
package com.example.http;
import java.util.*;
/**
* HTTP请求构建器 - Step Builder模式
*/
public class HttpRequest {
private final String url;
private final HttpMethod method;
private final Map<String, String> headers;
private final String body;
private final int timeoutSeconds;
private final boolean followRedirects;
public enum HttpMethod {
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
}
private HttpRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers));
this.body = builder.body;
this.timeoutSeconds = builder.timeoutSeconds;
this.followRedirects = builder.followRedirects;
}
// ============ Step接口定义 ============
public interface UrlStep {
MethodStep url(String url);
}
public interface MethodStep {
BuildStep method(HttpMethod method);
BuildStep get();
BuildStep post();
BuildStep put();
BuildStep delete();
}
public interface BuildStep {
BuildStep header(String key, String value);
BuildStep headers(Map<String, String> headers);
BuildStep body(String body);
BuildStep timeout(int seconds);
BuildStep followRedirects(boolean follow);
HttpRequest build();
}
// ============ Builder实现 ============
private static class Builder implements UrlStep, MethodStep, BuildStep {
private String url;
private HttpMethod method;
private Map<String, String> headers = new HashMap<>();
private String body;
private int timeoutSeconds = 30; // 默认30秒
private boolean followRedirects = true;
@Override
public MethodStep url(String url) {
if (url == null || url.trim().isEmpty()) {
throw new IllegalArgumentException("URL不能为空");
}
this.url = url;
return this;
}
@Override
public BuildStep method(HttpMethod method) {
this.method = method;
return this;
}
@Override
public BuildStep get() {
return method(HttpMethod.GET);
}
@Override
public BuildStep post() {
return method(HttpMethod.POST);
}
@Override
public BuildStep put() {
return method(HttpMethod.PUT);
}
@Override
public BuildStep delete() {
return method(HttpMethod.DELETE);
}
@Override
public BuildStep header(String key, String value) {
this.headers.put(key, value);
return this;
}
@Override
public BuildStep headers(Map<String, String> headers) {
this.headers.putAll(headers);
return this;
}
@Override
public BuildStep body(String body) {
this.body = body;
return this;
}
@Override
public BuildStep timeout(int seconds) {
if (seconds <= 0) {
throw new IllegalArgumentException("超时时间必须大于0");
}
this.timeoutSeconds = seconds;
return this;
}
@Override
public BuildStep followRedirects(boolean follow) {
this.followRedirects = follow;
return this;
}
@Override
public HttpRequest build() {
return new HttpRequest(this);
}
}
// ============ 静态工厂方法 ============
public static UrlStep builder() {
return new Builder();
}
// ============ 执行方法 ============
public void execute() {
System.out.println("执行HTTP请求:");
System.out.println(" URL: " + url);
System.out.println(" Method: " + method);
System.out.println(" Headers: " + headers);
System.out.println(" Body: " + body);
System.out.println(" Timeout: " + timeoutSeconds + "s");
System.out.println(" Follow Redirects: " + followRedirects);
}
// Getters
public String getUrl() { return url; }
public HttpMethod getMethod() { return method; }
public Map<String, String> getHeaders() { return headers; }
public String getBody() { return body; }
public int getTimeoutSeconds() { return timeoutSeconds; }
public boolean isFollowRedirects() { return followRedirects; }
}
使用示例:
java
public class HttpRequestDemo {
public static void main(String[] args) {
// GET请求
HttpRequest getRequest = HttpRequest.builder()
.url("https://api.example.com/users")
.get()
.header("Authorization", "Bearer token123")
.header("Accept", "application/json")
.timeout(60)
.build();
getRequest.execute();
System.out.println("\n" + "=".repeat(50) + "\n");
// POST请求
HttpRequest postRequest = HttpRequest.builder()
.url("https://api.example.com/users")
.post()
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token456")
.body("{\"name\":\"John\",\"email\":\"john@example.com\"}")
.timeout(30)
.followRedirects(false)
.build();
postRequest.execute();
System.out.println("\n" + "=".repeat(50) + "\n");
// PUT请求 - 使用便捷方法
HttpRequest putRequest = HttpRequest.builder()
.url("https://api.example.com/users/123")
.put()
.headers(Map.of(
"Content-Type", "application/json",
"Authorization", "Bearer token789"
))
.body("{\"name\":\"Jane\"}")
.build();
putRequest.execute();
}
}
四、开源框架中的Step Builder应用
4.1 OkHttp Request Builder分析
虽然OkHttp的Request.Builder不是严格的Step Builder,但我们可以用Step Builder改进它:
java
package com.example.okhttp;
import okhttp3.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 改进的OkHttp请求构建器
*/
public class StepOkHttpRequest {
private final Request request;
private final OkHttpClient client;
private StepOkHttpRequest(Builder builder) {
Request.Builder requestBuilder = new Request.Builder()
.url(builder.url);
// 设置HTTP方法和请求体
switch (builder.method) {
case GET:
requestBuilder.get();
break;
case POST:
requestBuilder.post(builder.requestBody);
break;
case PUT:
requestBuilder.put(builder.requestBody);
break;
case DELETE:
if (builder.requestBody != null) {
requestBuilder.delete(builder.requestBody);
} else {
requestBuilder.delete();
}
break;
}
// 添加请求头
builder.headers.forEach(requestBuilder::addHeader);
this.request = requestBuilder.build();
this.client = builder.client != null ? builder.client : new OkHttpClient();
}
public enum Method {
GET, POST, PUT, DELETE
}
// ============ Step接口定义 ============
public interface UrlStep {
MethodStep url(String url);
}
public interface MethodStep {
BuildStep get();
BodyStep post();
BodyStep put();
BuildStep delete();
}
public interface BodyStep {
BuildStep jsonBody(String json);
BuildStep formBody(Map<String, String> formData);
BuildStep emptyBody();
}
public interface BuildStep {
BuildStep header(String name, String value);
BuildStep client(OkHttpClient client);
StepOkHttpRequest build();
}
// ============ Builder实现 ============
private static class Builder implements UrlStep, MethodStep, BodyStep, BuildStep {
private String url;
private Method method;
private RequestBody requestBody;
private Map<String, String> headers = new HashMap<>();
private OkHttpClient client;
@Override
public MethodStep url(String url) {
this.url = url;
return this;
}
@Override
public BuildStep get() {
this.method = Method.GET;
return this;
}
@Override
public BodyStep post() {
this.method = Method.POST;
return this;
}
@Override
public BodyStep put() {
this.method = Method.PUT;
return this;
}
@Override
public BuildStep delete() {
this.method = Method.DELETE;
return this;
}
@Override
public BuildStep jsonBody(String json) {
this.requestBody = RequestBody.create(
json,
MediaType.parse("application/json; charset=utf-8")
);
return this;
}
@Override
public BuildStep formBody(Map<String, String> formData) {
FormBody.Builder formBuilder = new FormBody.Builder();
formData.forEach(formBuilder::add);
this.requestBody = formBuilder.build();
return this;
}
@Override
public BuildStep emptyBody() {
this.requestBody = RequestBody.create("", null);
return this;
}
@Override
public BuildStep header(String name, String value) {
this.headers.put(name, value);
return this;
}
@Override
public BuildStep client(OkHttpClient client) {
this.client = client;
return this;
}
@Override
public StepOkHttpRequest build() {
return new StepOkHttpRequest(this);
}
}
// ============ 静态工厂方法 ============
public static UrlStep builder() {
return new Builder();
}
// ============ 执行方法 ============
public Response execute() throws IOException {
return client.newCall(request).execute();
}
public void enqueue(Callback callback) {
client.newCall(request).enqueue(callback);
}
}
使用示例:
java
public class OkHttpStepDemo {
public static void main(String[] args) throws IOException {
// GET请求
StepOkHttpRequest getRequest = StepOkHttpRequest.builder()
.url("https://jsonplaceholder.typicode.com/posts/1")
.get()
.header("Accept", "application/json")
.build();
try (Response response = getRequest.execute()) {
System.out.println("GET Response: " + response.body().string());
}
// POST请求
StepOkHttpRequest postRequest = StepOkHttpRequest.builder()
.url("https://jsonplaceholder.typicode.com/posts")
.post()
.jsonBody("{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}")
.header("Content-Type", "application/json")
.build();
try (Response response = postRequest.execute()) {
System.out.println("POST Response: " + response.body().string());
}
}
}
4.2 Spring RestTemplate增强
java
package com.example.spring;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
/**
* Spring RestTemplate的Step Builder封装
*/
public class StepRestTemplate {
private final RestTemplate restTemplate;
private final String url;
private final HttpMethod method;
private final HttpHeaders headers;
private final Object body;
private StepRestTemplate(Builder builder) {
this.restTemplate = builder.restTemplate != null ?
builder.restTemplate : new RestTemplate();
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers;
this.body = builder.body;
}
// ============ Step接口定义 ============
public interface UrlStep {
MethodStep url(String url);
}
public interface MethodStep {
BuildStep get();
BodyStep post();
BodyStep put();
BuildStep delete();
}
public interface BodyStep {
BuildStep body(Object body);
BuildStep noBody();
}
public interface BuildStep {
BuildStep header(String name, String value);
BuildStep contentType(MediaType mediaType);
BuildStep accept(MediaType... mediaTypes);
BuildStep restTemplate(RestTemplate restTemplate);
<T> T execute(Class<T> responseType);
<T> ResponseEntity<T> executeForEntity(Class<T> responseType);
}
// ============ Builder实现 ============
private static class Builder implements UrlStep, MethodStep, BodyStep, BuildStep {
private RestTemplate restTemplate;
private String url;
private HttpMethod method;
private HttpHeaders headers = new HttpHeaders();
private Object body;
@Override
public MethodStep url(String url) {
this.url = url;
return this;
}
@Override
public BuildStep get() {
this.method = HttpMethod.GET;
return this;
}
@Override
public BodyStep post() {
this.method = HttpMethod.POST;
return this;
}
@Override
public BodyStep put() {
this.method = HttpMethod.PUT;
return this;
}
@Override
public BuildStep delete() {
this.method = HttpMethod.DELETE;
return this;
}
@Override
public BuildStep body(Object body) {
this.body = body;
return this;
}
@Override
public BuildStep noBody() {
this.body = null;
return this;
}
@Override
public BuildStep header(String name, String value) {
this.headers.add(name, value);
return this;
}
@Override
public BuildStep contentType(MediaType mediaType) {
this.headers.setContentType(mediaType);
return this;
}
@Override
public BuildStep accept(MediaType... mediaTypes) {
this.headers.setAccept(Arrays.asList(mediaTypes));
return this;
}
@Override
public BuildStep restTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
return this;
}
@Override
public <T> T execute(Class<T> responseType) {
StepRestTemplate request = new StepRestTemplate(this);
HttpEntity<?> entity = new HttpEntity<>(request.body, request.headers);
ResponseEntity<T> response = request.restTemplate.exchange(
request.url,
request.method,
entity,
responseType
);
return response.getBody();
}
@Override
public <T> ResponseEntity<T> executeForEntity(Class<T> responseType) {
StepRestTemplate request = new StepRestTemplate(this);
HttpEntity<?> entity = new HttpEntity<>(request.body, request.headers);
return request.restTemplate.exchange(
request.url,
request.method,
entity,
responseType
);
}
}
// ============ 静态工厂方法 ============
public static UrlStep builder() {
return new Builder();
}
}
五、高级技巧与最佳实践
5.1 泛型Step Builder
使用泛型可以创建更灵活的Step Builder:
java
package com.example.generic;
/**
* 泛型SQL查询构建器
*/
public class SqlQuery<T> {
private final String table;
private final Class<T> resultType;
private final String whereClause;
private final String orderBy;
private final Integer limit;
private SqlQuery(Builder<T> builder) {
this.table = builder.table;
this.resultType = builder.resultType;
this.whereClause = builder.whereClause;
this.orderBy = builder.orderBy;
this.limit = builder.limit;
}
// ============ Step接口定义 ============
public interface TableStep<T> {
ResultTypeStep<T> from(String table);
}
public interface ResultTypeStep<T> {
BuildStep<T> resultType(Class<T> clazz);
}
public interface BuildStep<T> {
BuildStep<T> where(String condition);
BuildStep<T> orderBy(String column);
BuildStep<T> limit(int limit);
SqlQuery<T> build();
List<T> execute();
}
// ============ Builder实现 ============
private static class Builder<T> implements TableStep<T>, ResultTypeStep<T>, BuildStep<T> {
private String table;
private Class<T> resultType;
private String whereClause;
private String orderBy;
private Integer limit;
@Override
public ResultTypeStep<T> from(String table) {
this.table = table;
return this;
}
@Override
public BuildStep<T> resultType(Class<T> clazz) {
this.resultType = clazz;
return this;
}
@Override
public BuildStep<T> where(String condition) {
this.whereClause = condition;
return this;
}
@Override
public BuildStep<T> orderBy(String column) {
this.orderBy = column;
return this;
}
@Override
public BuildStep<T> limit(int limit) {
this.limit = limit;
return this;
}
@Override
public SqlQuery<T> build() {
return new SqlQuery<>(this);
}
@Override
public List<T> execute() {
SqlQuery<T> query = build();
return query.executeQuery();
}
}
// ============ 静态工厂方法 ============
public static <T> TableStep<T> select() {
return new Builder<T>();
}
// ============ 执行方法 ============
public List<T> executeQuery() {
String sql = buildSql();
System.out.println("执行SQL: " + sql);
// 实际执行查询...
return new ArrayList<>();
}
private String buildSql() {
StringBuilder sql = new StringBuilder("SELECT * FROM ").append(table);
if (whereClause != null) {
sql.append(" WHERE ").append(whereClause);
}
if (orderBy != null) {
sql.append(" ORDER BY ").append(orderBy);
}
if (limit != null) {
sql.append(" LIMIT ").append(limit);
}
return sql.toString();
}
}
使用示例:
java
public class GenericBuilderDemo {
public static class UserDto {
private Long id;
private String name;
// getters and setters
}
public static void main(String[] args) {
// 类型安全的查询
List<UserDto> users = SqlQuery.<UserDto>select()
.from("users")
.resultType(UserDto.class)
.where("age > 18")
.orderBy("created_at DESC")
.limit(10)
.execute();
System.out.println("查询结果: " + users);
}
}
5.2 条件分支的Step Builder
有时需要根据条件选择不同的构建路径:
java
package com.example.conditional;
/**
* 支付请求构建器 - 支持多种支付方式
*/
public class PaymentRequest {
private final String orderId;
private final PaymentMethod method;
private final double amount;
// 支付宝特有字段
private final String alipayAccount;
// 微信特有字段
private final String wechatOpenId;
// 银行卡特有字段
private final String cardNumber;
private final String cardHolder;
public enum PaymentMethod {
ALIPAY, WECHAT, BANK_CARD
}
private PaymentRequest(BuilderBase builder) {
this.orderId = builder.orderId;
this.method = builder.method;
this.amount = builder.amount;
this.alipayAccount = builder instanceof AlipayBuilder ?
((AlipayBuilder) builder).alipayAccount : null;
this.wechatOpenId = builder instanceof WechatBuilder ?
((WechatBuilder) builder).wechatOpenId : null;
this.cardNumber = builder instanceof BankCardBuilder ?
((BankCardBuilder) builder).cardNumber : null;
this.cardHolder = builder instanceof BankCardBuilder ?
((BankCardBuilder) builder).cardHolder : null;
}
// ============ Step接口定义 ============
public interface OrderStep {
AmountStep orderId(String orderId);
}
public interface AmountStep {
MethodStep amount(double amount);
}
public interface MethodStep {
AlipayStep alipay();
WechatStep wechat();
BankCardStep bankCard();
}
public interface AlipayStep {
BuildStep account(String alipayAccount);
}
public interface WechatStep {
BuildStep openId(String wechatOpenId);
}
public interface BankCardStep {
CardHolderStep cardNumber(String cardNumber);
}
public interface CardHolderStep {
BuildStep cardHolder(String cardHolder);
}
public interface BuildStep {
PaymentRequest build();
}
// ============ Builder基类 ============
private static abstract class BuilderBase implements OrderStep, AmountStep, MethodStep {
protected String orderId;
protected double amount;
protected PaymentMethod method;
@Override
public AmountStep orderId(String orderId) {
this.orderId = orderId;
return this;
}
@Override
public MethodStep amount(double amount) {
this.amount = amount;
return this;
}
}
// ============ 具体Builder实现 ============
private static class AlipayBuilder extends BuilderBase implements AlipayStep, BuildStep {
private String alipayAccount;
@Override
public AlipayStep alipay() {
this.method = PaymentMethod.ALIPAY;
return this;
}
@Override
public WechatStep wechat() {
throw new UnsupportedOperationException();
}
@Override
public BankCardStep bankCard() {
throw new UnsupportedOperationException();
}
@Override
public BuildStep account(String alipayAccount) {
this.alipayAccount = alipayAccount;
return this;
}
@Override
public PaymentRequest build() {
return new PaymentRequest(this);
}
}
private static class WechatBuilder extends BuilderBase implements WechatStep, BuildStep {
private String wechatOpenId;
@Override
public AlipayStep alipay() {
throw new UnsupportedOperationException();
}
@Override
public WechatStep wechat() {
this.method = PaymentMethod.WECHAT;
return this;
}
@Override
public BankCardStep bankCard() {
throw new UnsupportedOperationException();
}
@Override
public BuildStep openId(String wechatOpenId) {
this.wechatOpenId = wechatOpenId;
return this;
}
@Override
public PaymentRequest build() {
return new PaymentRequest(this);
}
}
private static class BankCardBuilder extends BuilderBase
implements BankCardStep, CardHolderStep, BuildStep {
private String cardNumber;
private String cardHolder;
@Override
public AlipayStep alipay() {
throw new UnsupportedOperationException();
}
@Override
public WechatStep wechat() {
throw new UnsupportedOperationException();
}
@Override
public BankCardStep bankCard() {
this.method = PaymentMethod.BANK_CARD;
return this;
}
@Override
public CardHolderStep cardNumber(String cardNumber) {
this.cardNumber = cardNumber;
return this;
}
@Override
public BuildStep cardHolder(String cardHolder) {
this.cardHolder = cardHolder;
return this;
}
@Override
public PaymentRequest build() {
return new PaymentRequest(this);
}
}
// ============ 统一入口 ============
public static OrderStep builder() {
return new UnifiedBuilder();
}
private static class UnifiedBuilder extends BuilderBase {
@Override
public AlipayStep alipay() {
AlipayBuilder builder = new AlipayBuilder();
builder.orderId = this.orderId;
builder.amount = this.amount;
return builder.alipay();
}
@Override
public WechatStep wechat() {
WechatBuilder builder = new WechatBuilder();
builder.orderId = this.orderId;
builder.amount = this.amount;
return builder.wechat();
}
@Override
public BankCardStep bankCard() {
BankCardBuilder builder = new BankCardBuilder();
builder.orderId = this.orderId;
builder.amount = this.amount;
return builder.bankCard();
}
}
@Override
public String toString() {
return "PaymentRequest{" +
"orderId='" + orderId + '\'' +
", method=" + method +
", amount=" + amount +
", alipayAccount='" + alipayAccount + '\'' +
", wechatOpenId='" + wechatOpenId + '\'' +
", cardNumber='" + cardNumber + '\'' +
", cardHolder='" + cardHolder + '\'' +
'}';
}
}
使用示例:
java
public class PaymentDemo {
public static void main(String[] args) {
// 支付宝支付
PaymentRequest alipayPayment = PaymentRequest.builder()
.orderId("ORDER_001")
.amount(99.99)
.alipay()
.account("user@alipay.com")
.build();
System.out.println(alipayPayment);
// 微信支付
PaymentRequest wechatPayment = PaymentRequest.builder()
.orderId("ORDER_002")
.amount(199.99)
.wechat()
.openId("wx_openid_123456")
.build();
System.out.println(wechatPayment);
// 银行卡支付
PaymentRequest bankPayment = PaymentRequest.builder()
.orderId("ORDER_003")
.amount(299.99)
.bankCard()
.cardNumber("6222021234567890")
.cardHolder("Zhang San")
.build();
System.out.println(bankPayment);
}
}
5.3 验证增强
在build()方法中添加复杂验证逻辑:
java
public class ValidatedUser {
private final String username;
private final String password;
private final String email;
private final int age;
private ValidatedUser(Builder builder) {
this.username = builder.username;
this.password = builder.password;
this.email = builder.email;
this.age = builder.age;
}
// ... Step接口定义 ...
private static class Builder implements UsernameStep, PasswordStep, EmailStep, AgeStep, BuildStep {
private String username;
private String password;
private String email;
private int age;
// ... 各步骤实现 ...
@Override
public ValidatedUser build() {
// 用户名验证
if (username.length() < 3 || username.length() > 20) {
throw new IllegalArgumentException("用户名长度必须在3-20之间");
}
if (!username.matches("^[a-zA-Z0-9_]+$")) {
throw new IllegalArgumentException("用户名只能包含字母、数字和下划线");
}
// 密码验证
if (password.length() < 8) {
throw new IllegalArgumentException("密码长度不能少于8位");
}
if (!password.matches(".*[A-Z].*")) {
throw new IllegalArgumentException("密码必须包含至少一个大写字母");
}
if (!password.matches(".*[0-9].*")) {
throw new IllegalArgumentException("密码必须包含至少一个数字");
}
// 邮箱验证
if (!email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) {
throw new IllegalArgumentException("邮箱格式不正确");
}
// 年龄验证
if (age < 18 || age > 120) {
throw new IllegalArgumentException("年龄必须在18-120之间");
}
return new ValidatedUser(this);
}
}
public static UsernameStep builder() {
return new Builder();
}
}
六、Step Builder vs 其他模式
6.1 对比表格
特性对比:
┌────────────────┬──────────────┬──────────────┬──────────────┐
│ 特性 │ Step Builder │ 传统Builder │ 构造函数 │
├────────────────┼──────────────┼──────────────┼──────────────┤
│ 必填参数检查 │ 编译期 │ 运行期 │ 编译期 │
├────────────────┼──────────────┼──────────────┼──────────────┤
│ 可读性 │ ★★★★★ │ ★★★★☆ │ ★★☆☆☆ │
├────────────────┼──────────────┼──────────────┼──────────────┤
│ 类型安全 │ ★★★★★ │ ★★★☆☆ │ ★★★★★ │
├────────────────┼──────────────┼──────────────┼──────────────┤
│ 实现复杂度 │ ★★★★☆ │ ★★☆☆☆ │ ★☆☆☆☆ │
├────────────────┼──────────────┼──────────────┼──────────────┤
│ 扩展性 │ ★★★★★ │ ★★★★☆ │ ★★☆☆☆ │
├────────────────┼──────────────┼──────────────┼──────────────┤
│ IDE支持 │ ★★★★★ │ ★★★★☆ │ ★★★☆☆ │
└────────────────┴──────────────┴──────────────┴──────────────┘
6.2 选择指南
使用Step Builder的场景:
- ✅ 对象有多个必填参数
- ✅ 需要编译期保证参数完整性
- ✅ 构建过程有明确的步骤顺序
- ✅ API需要高度的类型安全
使用传统Builder的场景:
- ✅ 大部分参数都是可选的
- ✅ 参数没有明确的顺序要求
- ✅ 需要简单实现
使用构造函数的场景:
- ✅ 参数很少(1-3个)
- ✅ 所有参数都是必填的
- ✅ 对象创建非常简单
七、实际生产经验
7.1 Lombok集成
可以使用Lombok减少样板代码:
java
import lombok.Builder;
import lombok.Value;
// 注意:Lombok的@Builder不支持Step Builder
// 需要手动实现Step接口
@Value
public class Product {
String id;
String name;
double price;
String category;
// 手动实现Step Builder
public static IdStep builder() {
return new StepBuilder();
}
// Step接口定义...
private static class StepBuilder implements IdStep, NameStep, PriceStep, CategoryStep, BuildStep {
private String id;
private String name;
private double price;
private String category;
// 实现各步骤方法...
@Override
public Product build() {
return new Product(id, name, price, category);
}
}
}
7.2 测试友好性
Step Builder模式使测试数据构建更加清晰:
java
public class UserTest {
@Test
public void testUserCreation() {
// 测试数据构建清晰明了
User validUser = User.builder()
.username("testuser")
.password("TestPass123")
.email("test@example.com")
.age(25)
.build();
assertNotNull(validUser);
assertEquals("testuser", validUser.getUsername());
}
@Test
public void testInvalidEmail() {
// 编译器强制设置所有必填字段
assertThrows(IllegalArgumentException.class, () -> {
User.builder()
.username("testuser")
.password("TestPass123")
.email("invalid-email") // 无效邮箱
.age(25)
.build();
});
}
}
7.3 性能考虑
Step Builder的性能开销主要来自:
- 多个接口的实现
- 对象创建时的类型转换
优化建议:
java
// 1. 使用final字段减少内存占用
private static class Builder implements ... {
private final StringBuilder urlBuilder = new StringBuilder();
// ...
}
// 2. 对于高频调用,考虑对象池
public class ObjectPooledBuilder {
private static final ThreadLocal<Builder> POOL =
ThreadLocal.withInitial(Builder::new);
public static BuilderStep builder() {
Builder builder = POOL.get();
builder.reset();
return builder;
}
}
// 3. 延迟验证到build()方法
@Override
public User build() {
validateAll(); // 只在最后验证一次
return new User(this);
}
八、常见问题与解决方案
8.1 如何处理可选参数组
java
public interface BuildStep {
// 方案1: 单独的可选参数方法
BuildStep optionalField1(String value);
BuildStep optionalField2(String value);
// 方案2: 配置对象
BuildStep withOptions(Options options);
User build();
}
public class Options {
private String field1;
private String field2;
public static Options create() {
return new Options();
}
public Options field1(String value) {
this.field1 = value;
return this;
}
public Options field2(String value) {
this.field2 = value;
return this;
}
}
8.2 如何支持不可变对象
java
@Value // Lombok注解,生成不可变对象
public class ImmutableUser {
String username;
String password;
String email;
// Step Builder确保所有必填字段都被设置
// build()返回不可变对象
}
8.3 如何处理继承
java
public class Employee extends Person {
private final String employeeId;
private final String department;
// 继承父类的Step Builder
public static EmployeeIdStep builder() {
return new Builder();
}
// 扩展父类的Step接口
public interface EmployeeIdStep extends Person.NameStep {
DepartmentStep employeeId(String id);
}
public interface DepartmentStep {
Person.BuildStep department(String dept);
}
private static class Builder extends Person.Builder
implements EmployeeIdStep, DepartmentStep {
private String employeeId;
private String department;
@Override
public DepartmentStep employeeId(String id) {
this.employeeId = id;
return this;
}
@Override
public Person.BuildStep department(String dept) {
this.department = dept;
return this;
}
@Override
public Employee build() {
return new Employee(this);
}
}
}
九、总结
9.1 核心优势
- 编译期安全:在编译时就能发现缺少必填参数的错误
- 清晰的API:IDE自动提示引导开发者完成所有步骤
- 类型安全:利用类型系统保证调用顺序
- 自文档化:代码本身就说明了构建流程
9.2 适用场景总结
推荐使用Step Builder的情况:
├─ 对象有3个以上必填参数
├─ 需要严格的参数验证
├─ 构建过程有明确的步骤顺序
├─ 团队希望提高代码质量
└─ 公共API需要防止误用
不推荐使用的情况:
├─ 简单对象(参数少于3个)
├─ 参数都是可选的
├─ 快速原型开发
└─ 性能极度敏感的场景
9.3 最佳实践清单
- ✅ 为每个必填参数创建独立的Step接口
- ✅ 将可选参数放在最后的BuildStep中
- ✅ 在build()方法中进行最终验证
- ✅ 使用有意义的接口命名(如UsernameStep)
- ✅ 提供清晰的JavaDoc文档
- ✅ 考虑使用代码生成工具减少重复代码
- ❌ 不要在Step方法中进行复杂验证
- ❌ 不要过度使用(简单对象用普通Builder)