模式简介
建造者模式是一种常见的设计模式,属于创建型模式,我们常见的工厂、单例、原型、包括《重构与模式》一书中提到的creation method都属于创建型模式。建造者适合用于构建复杂对象,它可以将创建和表示分离,使我们的代码可读性更好,更易于维护。
坏味道代码
在我们的实际开发中,经常会构建一些实体对象,比如,在笔者参与的交易系统中,会记录用户的每一笔支付,我们在创建支付实体对象时,通常会写出这样的代码:
PaymentRecord paymentRecord = new PaymentRecord();
// 设置一些基础属性
paymentRecord.setId(sequence.nextValue());
paymentRecord.setUserId(...);
...
// 计算价格
Long discountFee = ...; // 促销金额
Long totalFee = calculateTotalFee(..., discountFee); // 计算总金额
paymentRecord.setTotalFee(totalFee);
paymentRecord.setDiscountFee(discountFee);
...
// 一些扩展属性的设置
...
// 不同的业务的一些个性化参数设置
...
我们会发现,这样一个对象的构建过程非常的复杂,每次需要对这里做一些改动时,都需要埋头于这一堆繁琐的代码中苦苦的寻找,可读性非常差,维护起来相当麻烦,而这样的代码在我们的实际开发过程中是非常常见的,我们可以利用建造者模式来进行一次重构。
重构代码实现
首先,抽象建造者:
/**
* PaymentRecord 建造者
*/
public interface PaymentRecordBuilder {
/**
* 构建payment record
*/
PaymentRecord build();
}
接下来根据不同的构建需求实现不同的建造者:
/**
* 根据支付请求构建 PaymentRecord
*/
public class RequestPaymentRecordBuilder implements PaymentRecordBuilder {
private PaymentRequest paymentRequest;
public RequestPaymentRecordBuilder(PaymentRequest paymentRequest) {
this.paymentRequest = paymentRequest;
}
@Override
public PaymentRecord build() {
PaymentRecord paymentRecord = new PaymentRecord();
buildBase(paymentRecord);
buildFee(paymentRecord);
buildExtension(paymentRecord);
buildBiz(paymentRecord);
return paymentRecord;
}
/**
* 构建基础属性
*/
private void buildBase(PaymentRecord paymentRecord) {
paymentRecord.setId(sequence.nextValue());
paymentRecord.setUserId(paymentRequest.getUserId());
...
}
/**
* 费用
*/
private void buildFee(PaymentRecord paymentRecord) {
// 获取促销金额
Long discountFee = ...;
// 计算总金额
Long totalFee = calculateTotalFee(..., discountFee);
paymentRecord.setTotalFee(totalFee);
paymentRecord.setDiscountFee(discountFee);
...
}
/**
* 构建扩展属性
*/
private void buildExtension(PaymentRecord paymentRecord) {
...
}
/**
* 构建不同业务相关
*/
private void buildBiz(PaymentRecord paymentRecord) {
...
}
}
这样整个实体对象的构建过程就非常清晰,将整个构建过程拆分开来,相互独立,层次很分明,每一个子构建过程如果还是比较复杂,同样可以抽象另外的建造者进行构建。这样看似简单的重构,却能大大的提高程序的可读性和可维护性,然后我们就可以使用这样的方式来构建实体对象:
PaymentRecord paymentRecord = new RequestPaymentRecordBuilder(paymentRequest).build();