我们日常会遇到一些问题,比如一个对象有很多属性,比如订单,有很多属性订单号,订单日期,订单用户姓名,订单状态啥的,我们要构建一个这个样的对象,要不断的去set,get 如下:
public class Order {
private String orderNo;
private Long orderId;
private LocalDate confirmDate;
private String templateId;
private Integer orderStatus;
}
在不考虑什么设计模式的情况下,这种“繁琐”的塞值怎么办,一种是创建带参数的构造函数,一种是找个别的类做转换直接转换过去。
创建带参数的构造函数时会遇到参数太多,这个函数很长看起来很不友好的情况,而且会遇到我有时候创建需要三个,有时候需要两个参数,要不然就赋予参数的值的时候,直接就按最长的来,大不了用不到的位置给个null,但是总之还是很不灵活。
一般来说,我们超过三个参数,就觉得方法参数过多设计的入参不友好。
public order(String orderNo,Long orderId,LocalDate confirmDate, String templateId,Integer orderStatus){
//todo
}
转换那个,也就平时传参的时候VO转DO会这样,但是VO实际上也是要塞值的。
那问题来了,有没有一种方法,能像我们的linux的管道命令一样,给一个对象赋值这样:
new Order().XXX().XXX().XXX()
当然有,如果你用过都多的第三方组件,不管是接口API还是那种RPC或者其他网络调用,都会有类似的
//这是webclient的一个链式调用的
ShoppingCart checkoutInfo = webClientBuilder.build()
.post()
.uri("http://coupon-calculation-serv/calculator/checkout")
.bodyValue(order)
.retrieve()
.bodyToMono(ShoppingCart.class)
.block();
我们可不可以也搞一个这样的代码呢,这种模式是什么?
答案肯定是没问题,我们首先来个简单的,这种很常见在我们第三方API或者分页插件的Page设计。
比如这个PageVO的设计
public class PageVO implements Serializable {
private static final long serialVersionUID = 34234234234985L;
@JsonSerialize(
using = LongSerializer.class
)
private long total;
@JsonSerialize(
using = LongSerializer.class
)
private long pageSize;
@JsonSerialize(
using = LongSerializer.class
)
private long pageCount;
@JsonSerialize(
using = LongSerializer.class
)
private long pageNum;
private boolean isHasNextPage;
private boolean isHasPreviousPage;
private List list;
private void build(long pageSize, long pageNum, long total, long pageCount, List> records, Class clazz) {
this.pageSize = pageSize;
this.pageNum = pageNum;
this.total = total;
this.pageCount = pageCount;
this.isHasNextPage = pageCount > pageNum;
this.isHasPreviousPage = pageNum != 1L;
List list = EntityConverter.convertList(records, clazz);
this.list = list;
}
private void build(long pageSize, long pageNum, long total, long pageCount, List records) {
this.pageSize = pageSize;
this.pageNum = pageNum;
this.total = total;
this.pageCount = pageCount;
this.isHasNextPage = pageCount > pageNum;
this.isHasPreviousPage = pageNum != 1L;
this.list = records;
}
private void build(long pageSize, long pageNum, long total, List> records, Class clazz) {
this.pageSize = pageSize;
this.pageNum = pageNum;
this.total = total;
this.setPageCount();
this.isHasNextPage = this.pageCount > pageNum;
this.isHasPreviousPage = pageNum != 1L;
List list = EntityConverter.convertList(records, clazz);
this.list = list;
}
private void build(long pageSize, long pageNum, long total, List records) {
this.pageSize = pageSize;
this.pageNum = pageNum;
this.total = total;
this.setPageCount();
this.isHasNextPage = this.pageCount > pageNum;
this.isHasPreviousPage = pageNum != 1L;
this.list = records;
}
public PageVO(IPage> iPage, Class clazz) {
this.build(iPage.getSize(), iPage.getCurrent(), iPage.getTotal(), iPage.getPages(), iPage.getRecords(), clazz);
}
public PageVO(IPage iPage) {
this.build(iPage.getSize(), iPage.getCurrent(), iPage.getTotal(), iPage.getPages(), iPage.getRecords());
}
public PageVO(PageQuery pageQuery, long total, List> records, Class clazz) {
this.build(pageQuery.getPageSize(), pageQuery.getPageNum(), total, records, clazz);
}
public PageVO(PageQuery pageQuery, long total, List records) {
this.build(pageQuery.getPageSize(), pageQuery.getPageNum(), total, records);
}
public PageVO(long pageSize, long pageNum, long total, List records, Class clazz) {
this.build(pageSize, pageNum, total, records, clazz);
}
public PageVO(long pageSize, long pageNum, long total, List records) {
this.build(pageSize, pageNum, total, records);
}
private void setPageCount() {
if (this.total == 0L) {
this.pageCount = 0L;
} else {
long i = this.total % this.pageSize;
long count = this.total / this.pageSize;
this.pageCount = i == 0L ? count : count + 1L;
}
}
public PageVO() {
}
public long getTotal() {
return this.total;
}
public void setTotal(long total) {
this.total = total;
}
public long getPageSize() {
return this.pageSize;
}
public void setPageSize(long pageSize) {
this.pageSize = pageSize;
}
public long getPageCount() {
return this.pageCount;
}
public void setPageCount(long pageCount) {
this.pageCount = pageCount;
}
public long getPageNum() {
return this.pageNum;
}
public void setPageNum(long pageNum) {
this.pageNum = pageNum;
}
public boolean isHasNextPage() {
return this.isHasNextPage;
}
public void setHasNextPage(boolean hasNextPage) {
this.isHasNextPage = hasNextPage;
}
public boolean isHasPreviousPage() {
return this.isHasPreviousPage;
}
public void setHasPreviousPage(boolean hasPreviousPage) {
this.isHasPreviousPage = hasPreviousPage;
}
public List getList() {
return this.list;
}
public void setList(List list) {
this.list = list;
}
}
当然感觉有点看不动,参数有点多看一下我的一个简单的关于订单的demo
package com.example.test.builder;
import lombok.Data;
import java.time.LocalDate;
/**
* @Author: zhangpeng
* @Description:构建者模式
* @Date: 2022/5/19
*/
@Data
public class Order {
private String orderNo;
private Long orderId;
private LocalDate confirmDate;
private String templateId;
private Integer orderStatus;
public static class Builder{
private String orderNo;
private Long orderId;
private LocalDate confirmDate;
private String templateId;
private Integer orderStatus;
public Builder(){
}
public Order build(){
return new Order(this);
}
public Builder setOrderNo(String orderNo){
this.orderNo = orderNo;
return this;
}
public Builder setOrderId(Long orderId){
this.orderId = orderId;
return this;
}
public Builder setConfirmDate(LocalDate localDate){
this.confirmDate = localDate;
return this;
}
public Builder setTemplateId(String templateId){
this.templateId = templateId;
return this;
}
public Builder setOrderStatus(Integer orderStatus){
this.orderStatus = orderStatus;
return this;
}
}
private Order(Builder builder){
this.orderId = builder.orderId;
this.orderNo = builder.orderNo;
this.confirmDate = builder.confirmDate;
this.templateId = builder.templateId;
this.orderStatus = builder.orderStatus;
}
}
他的简单使用
/**
* @Author: zhangpeng
* @Description:
* @Date: 2022/5/19
*/
public class OrderTest {
public static void main(String[] args) {
OrderTest test = new OrderTest();
Order order = new Order.Builder().setOrderNo("orderXXXX")
.setConfirmDate(LocalDate.now())
.setOrderStatus(1)
.build();
System.out.println(JSONObject.toJSONString(order));
}
}
实际上,很简单,就是再Order里面有一个builder类,用它来创建一个Order实例(听起来是不是有点像代理模式),bulider大概分成四部分:
1 一个简单的内部类,里面的属性和Order属性相同
2 内部类的构造函数,这里我是直接搞了个空的,可以根据实际情况来处理(我后面的正规的构造者模式会用到)
3 bulid方法,真正核心的一个方法,他直接返回一个Order实例
4 属性方法,这些都是平行的方法,谁前谁后无所谓的
这里妙就妙在一个内部类,一个通过bulider对象来构建的构造方法,搭建起来这个可以链式调用的。
上面的方式是我们常见的,但是真正书本上的创建者模式要更繁琐一些,不是一个类里面加一个内部类,一个内部类的构造方法就完事的。
这是我找的一个构建者模式的UML图
我们通过正规传统的构建者模式,其实更能解构出来他的大体结构和作用
由图director builder XXBuilder Product
我想要获取一个Productor的实例是这样的
我先创建一个director(director的作用就是管理Builder)
创建一个Bulider,这个Bulider是一个抽象类(我们可以根据他进行各种类型的扩展)
创建XXXBuilder,一个具体的实例(主要是生成定制化的Productor)
Prductor只是一个对象而已,中间甚至都不用去创建它,bulider的build方法就可以。
首先针对创建对象这种,超过四个参数就要考虑用创建者模式。
创建者模式,同样正对某些工具API也是一样,我调用某个工具,需要定制某些属性,直接添加就可以,彼此之间不会有联系,像我们mybatis的lambda其实就是这样,我们可以通过这种设计,让调用变的更简单已用,而不是自己搞一个构造函数,一个个去研究参数。
LambdaQueryWrapper queryErpBegin = new LambdaQueryWrapper<>();
queryErpBegin.eq(ErpBeginningConfigDO::getErpCustomerNo,customerNo)
.eq(ErpBeginningConfigDO::getDataStatus, DataStatus.DATA_STATUS_ENABLE.getValue())
.eq(ErpBeginningConfigDO::getMerchantId,merchantId);
附上我的GitHub对应的代码
github 构造者模式