设计模式:创建者模式(链式调用)

我们日常会遇到一些问题,比如一个对象有很多属性,比如订单,有很多属性订单号,订单日期,订单用户姓名,订单状态啥的,我们要构建一个这个样的对象,要不断的去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图
设计模式:创建者模式(链式调用)_第1张图片
我们通过正规传统的构建者模式,其实更能解构出来他的大体结构和作用

由图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 构造者模式

你可能感兴趣的:(设计模式,设计模式,java,开发语言)