Builder pattern (Builder)

1. Definitions

Separating the construction of a complex object from its representation allows the same construction process to create different representations.

2. Structure

write picture description here
- Builder: Builder interface that defines the operations of each component required to create a Product object.
- ConcreteBuilder: A concrete generator implementation, which realizes the creation of each component, and is responsible for assembling each component of the Product object, and also provides a method for users to obtain the assembled product object.
- Director: The director, also known as the director, is mainly used to use the Builder interface to build the required Product objects in a unified process.
- Product: A product, representing a complex object constructed by the generator, containing multiple parts.

3. Examples

The generator mode is used as an example of exporting data below: Usually, there are some constraints on exporting data, such as exporting to text format, database backup format, Excel format, Xml format, etc. Usually, there are requirements for the specific export content and format. The following requirements are now added. Briefly describe:
- The exported file, no matter what format, is divided into three parts, namely the file header, the file body, and the file tail.
- In the header part of the file, you need to describe the following information: branch or store number, date of exporting data.
- In the file body part, you need to describe the following information: table name, and then describe the data in sections.
- At the end of the file, the following information needs to be described: output person.

  1. The data objects that describe the various parts of the file are defined below.
/**
 * 描述输出到文件头的内容的对象
 * @author lijun
 * @since 2018-03-29 10:10
 */
public class ExportHeaderModel {
    /**
     * 分公司或者门市编号
     */
    private String depId;
    /**
     * 导出数据的日期
     */
    private String exportDate;

    public String getDepId() {
        return depId;
    }
    public void setDepId(String depId) {
        this.depId = depId;
    }
    public String getExportDate() {
        return exportDate;
    }
    public void setExportDate(String exportDate) {
        this.exportDate = exportDate;
    }
}
/**
 * 描述输出数据的对象
 * @author lijun
 * @since 2018-03-29 10:11
 */
public class ExportDataModel {
    /**
     * 产品编号
     */
    private String productId;
    /**
     * 销售价格
     */
    private double price;
    /**
     * 销售数量
     */
    private double amount;

    public String getProductId() {
        return productId;
    }
    public void setProductId(String productId) {
        this.productId = productId;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public double getAmount() {
        return amount;
    }
    public void setAmount(double amount) {
        this.amount = amount;
    }
}
/**
 * 描述输出到文件尾的内容的对象
 * @author lijun
 * @since 2018-03-29 10:12
 */
public class ExportFooterModel {
    /**
     * 输出人
     */
    private String exportUser;

    public String getExportUser() {
        return exportUser;
    }

    public void setExportUser(String exportUser) {
        this.exportUser = exportUser;
    }
}
  1. Defining the Builder interface mainly defines the steps in the process of exporting files in various formats, and each step is responsible for building a part of the final export file. The sample code is as follows:
**
 * 生成器接口,定义创建一个输出文件对象所需的各个部件的操作
 * @author lijun
 * @since 2018-03-29 10:17
 */
public interface Builder {
    /**
     * 构建输出文件的Header部分
     * @param ehm
     */
    public void buildHeader(ExportHeaderModel ehm);

    /**
     * 构建输出文件的Body部分
     * @param mapData
     */
    public void buildBody(Map<String,List<ExportDataModel>> mapData);

    /**
     * 构建输出文件的Footer部分
     * @param efm
     */
    public void buildFooter(ExportFooterModel efm);
}
  1. Concrete generator implementation.

Generator implementation for exporting to a text file. The sample code is as follows:

/**
 * 实现导出文件到文本文件的生成器对象 
 * @author lijun
 * @since 2018-03-29 10:24
 */
public class TxtBuilder implements Builder {
    /**
     * 用来记录构建的文件的内容,相当于产品
     */
    private StringBuffer buffer = new StringBuffer();

    @Override
    public void buildHeader(ExportHeaderModel ehm) {
        buffer.append(ehm.getDepId() + "," + ehm.getExportDate() + "\n");
    }

    @Override
    public void buildBody(Map<String, List<ExportDataModel>> mapData) {
        for (String tablName : mapData.keySet()) {

            //先拼接表名
            buffer.append(tablName + "\n");
            //然后循环拼接具体数据
            for (ExportDataModel edm : mapData.get(tablName)) {
                buffer.append(edm.getProductId() + "," + edm.getPrice() + "," + edm.getAmount() + "\n");
            }
        }
    }

    @Override
    public void buildFooter(ExportFooterModel efm) {
        buffer.append(efm.getExportUser());
    }

    public StringBuffer getResult() {
        return buffer;
    }

}

Generator implementation for exporting to Xml files. The sample code is as follows:

/**
 * 实现导出文件到Xml文件的生成器对象
 *
 * @author lijun
 * @since 2018-03-29 10:27
 */
public class XmlBuilder implements Builder {
    /**
     * 用来记录构建的文件的内容,相当于产品
     */
    private StringBuffer buffer = new StringBuffer();

    @Override
    public void buildHeader(ExportHeaderModel ehm) {
        buffer.append("<?xml version='1.0' encoding='UTF-8'?>\n");
        buffer.append("<Report>\n");
        buffer.append("\t<Header>\n");
        buffer.append("\t\t<DepId>" + ehm.getDepId() + "</DepId>\n");
        buffer.append("\t\t<ExportDate>" + ehm.getExportDate() + "</ExportDate>\n");

        buffer.append("\t</Header>\n");
    }

    @Override
    public void buildBody(Map<String, List<ExportDataModel>> mapData) {
        buffer.append("\t<Body>\n");
        for (String tablName : mapData.keySet()) {
            //先拼接表名
            buffer.append("\t\t<Datas TableName=\"" + tablName + "\">\n");
            //然后循环拼接具体数据
            for (ExportDataModel edm : mapData.get(tablName)) {

                buffer.append("\t\t\t<Data>\n");

                buffer.append("\t\t\t\t<ProductId>" + edm.getProductId() + "</ProductId>\n");
                buffer.append("\t\t\t\t<Price>" + edm.getPrice() + "</Price>\n");
                buffer.append("\t\t\t\t<Amount>" + edm.getAmount() + "</Amount>\n");

                buffer.append("\t\t\t</Data>\n");
            }

            buffer.append("\t\t</Datas>\n");
        }
        buffer.append("\t</Body>\n");
    }

    @Override
    public void buildFooter(ExportFooterModel efm) {
        buffer.append("\t<Footer>\n");
        buffer.append("\t\t<ExportUser>" + efm.getExportUser() + "</ExportUser>\n");
        buffer.append("\t</Footer>\n");
        buffer.append("</Report>\n");
    }

    public StringBuffer getResult() {
        return buffer;
    }

}
  1. Director. With a specific generator implementation, it needs to be guided by a mentor to build a specific product. The sample code is as follows:
/**
 * 指导者,指导使用生成器的接口来构建输出的文件对象
 * @author lijun
 * @since 2018-03-29 10:29
 */
public class Director {
    /**
     * 持有当前需要的使用的生成器对象
     */
    private Builder builder;

    /**
     * 构造方法,传入生成器对象
     *
     * @param builder
     */
    public Director(Builder builder) {
        this.builder = builder;
    }

    public void construct(ExportHeaderModel ehm,
                          Map<String, List<ExportDataModel>> mapData, ExportFooterModel efm) {
        //1.先构建Header
        builder.buildHeader(ehm);

        //2.然后构建Body
        builder.buildBody(mapData);

        //3.再构建Footer
        builder.buildFooter(efm);
    }
}
  1. The client test code is as follows:
/**
 * @author lijun
 * @since 2018-03-29 10:31
 */
public class Client {
    /**
     * @param args
     */
    public static void main(String[] args) {

        //准备测试数据
        ExportHeaderModel ehm = new ExportHeaderModel();
        ehm.setDepId("一分公司");
        ehm.setExportDate("2018-03-29");

        Map<String, List<ExportDataModel>> mapData = new HashMap<String, List<ExportDataModel>>();
        List<ExportDataModel> col = new ArrayList<ExportDataModel>();

        ExportDataModel edm1 = new ExportDataModel();
        edm1.setProductId("产品001号");
        edm1.setPrice(100);
        edm1.setAmount(80);

        ExportDataModel edm2 = new ExportDataModel();
        edm2.setProductId("产品002号");
        edm2.setPrice(120);
        edm2.setAmount(280);

        ExportDataModel edm3 = new ExportDataModel();
        edm3.setProductId("产品003号");
        edm3.setPrice(320);
        edm3.setAmount(380);

        col.add(edm1);
        col.add(edm2);
        col.add(edm3);

        mapData.put("销售记录表", col);

        ExportFooterModel efm = new ExportFooterModel();
        efm.setExportUser("LIJUN");

        //测试输出到文本文件
        TxtBuilder txtBuilder = new TxtBuilder();
        //创建指导者对象
        Director director = new Director(txtBuilder);
        director.construct(ehm, mapData, efm);

        //把要输出的内容输出到控制台看看
        System.out.println("输出到文本文件的内容:" + txtBuilder.getResult().toString());

        XmlBuilder xmlBuilder = new XmlBuilder();
        Director director2 = new Director(xmlBuilder);
        director2.construct(ehm, mapData, efm);

        //把要输出的内容输出到控制台看看
        System.out.println("输出到Xml文件的内容:" + xmlBuilder.getResult().toString());
    }

}

Build complex objects using the generator pattern.

Consider such a practical application. To create an insurance contract object, the values ​​of many attributes in it are constrained, and the created object is required to meet these constraint rules. For example, insurance contracts can usually be signed with individuals, or they can be Signed with a company, but an insurance contract cannot be signed with an individual and a company at the same time. There are many constraints like this in this object, so how to create this object?
If you want to create this object in a concise and intuitive way, with good security and good scalability, a better choice is to use the Builder mode to realize the complex creation process through the Builder.
Using the Builder pattern to build complex objects usually simplifies the Builder pattern to a certain extent, because the goal is to create a complex object, so proper simplification will make the program more concise. Roughly simplified as follows:
- Use the Builder pattern to create an object, so there is no need to define a Builder interface, just provide a specific builder class directly.
- For creating a complex object, there may be many different options and steps. Simply remove the "instructor" and combine the functions of the instructor and the client. That is to say, the client is equivalent to the instructor at this time. , which guides the builder class to construct the required complex objects. For the simplicity of the example, let's not consider the implementation of constraints, but only consider how to build complex objects through the Builder pattern.
1. Use the Builder pattern to build complex objects, regardless of constraints.

(1) Objects of insurance contracts

/**
 * 保险合同对象
 *
 * @author lijun
 * @since 2018-03-29 13:18
 */
public class Insurancecontract {

    /**
     * 保险合同编号
     */
    private String contractId;

    /**
     * 被保人名称 同一份保险合同,要么跟人员签订,要么跟公司签订,也就是说,“被保险人员”和“被保公司"
     * 这个两个属性不可能同时有值。
     */
    private String personName;

    /**
     * 被保公司
     */
    private String companyName;

    /**
     * 保险开始生效的时间
     */
    private long beginDate;

    /**
     * 保险失效时间
     */
    private long endDate;


    /**
     * 其他数据
     */
    private String otherData;


     Insurancecontract(ConcreteBuilder concreteBuilder) {
       this.contractId = concreteBuilder.getContractId();
       this.beginDate = concreteBuilder.getBeginDate();
       this.companyName = concreteBuilder.getCompanyName();
       this.endDate = concreteBuilder.getEndDate();
       this.otherData = concreteBuilder.getOtherData();
       this.personName = concreteBuilder.getPersonName();
    }

    /**
     * 保险合同的某些操作
     */
    public void someOperation() {
        System.out.println("一些操作 " + this.contractId);
    }
}

(1) ConcreteBuilder, a builder for building insurance contract objects

/**
 * 构建保险合同对象的构建器
 *
 * @author lijun
 * @since 2018-03-29 14:11
 */
public class ConcreteBuilder {

    /**
     * 保险合同编号
     */
    private String contractId;

    /**
     * 被保人名称 同一份保险合同,要么跟人员签订,要么跟公司签订,也就是说,“被保险人员”和“被保公司"
     * 这个两个属性不可能同时有值。
     */
    private String personName;

    /**
     * 被保公司
     */
    private String companyName;

    /**
     * 保险开始生效的时间
     */
    private long beginDate;

    /**
     * 保险失效时间
     */
    private long endDate;


    /**
     * 其他数据
     */
    private String otherData;

    public ConcreteBuilder(String contractId, long beginDate, long endDate) {
        this.contractId = contractId;
        this.beginDate = beginDate;
        this.endDate = endDate;
    }

    public ConcreteBuilder setPersonName(String personName) {
        this.personName = personName;
        return this;
    }

    public ConcreteBuilder setCompanyName(String companyName) {
        this.companyName = companyName;
        return this;
    }

    public ConcreteBuilder setOtherData(String otherData) {
        this.otherData = otherData;
        return this;
    }

    public String getContractId() {
        return contractId;
    }

    public void setContractId(String contractId) {
        this.contractId = contractId;
    }

    public String getPersonName() {
        return personName;
    }


    public String getCompanyName() {
        return companyName;
    }


    public long getBeginDate() {
        return beginDate;
    }

    public void setBeginDate(long beginDate) {
        this.beginDate = beginDate;
    }

    public long getEndDate() {
        return endDate;
    }

    public void setEndDate(long endDate) {
        this.endDate = endDate;
    }

    public String getOtherData() {
        return otherData;
    }

    /**
     * 构建真正的对象并返回
     *
     * @return 构建的保险合同对象
     */
    public Insurancecontract build() {
        return new Insurancecontract(this);
    }

}

(3)Client

public class Client {
    public static void main(String[] args) {

        ConcreteBuilder concreteBuilder = new ConcreteBuilder("1000", 8999L, 67890L);
        Insurancecontract insurancecontract = concreteBuilder.setPersonName("LIJUN").setOtherData("Test").build();
        insurancecontract.someOperation();
    }
}
  1. Use the Builder pattern to build complex objects, considering rules with constraints.
    The implementation of the rules with constraints is not different from the implementation just now, but the constraints need to be added to the implementation just now. Just add constraints to the method build of ConcreteBuilder.
  /**
     * 构建真正的对象并返回
     *
     * @return 构建的保险合同对象
     */
    public Insurancecontract build() {
        if (contractId == null || contractId.trim().length() == 0) {
            throw new IllegalArgumentException("合同编号不能为空!");
        }
        boolean personIs = personName == null || personName.trim().length() == 0;
        boolean companyIs = companyName == null || companyName.trim().length() == 0;

        if (personIs && companyIs) {
            throw new IllegalArgumentException("一份合同不能同时和个人和公司同签!");
        }

        if (personIs == false && companyIs == false) {
            throw new IllegalArgumentException("一份合同不能没有签订对象");
        }

        if (beginDate <= 0) {
            throw new IllegalArgumentException("保险合同必须有开始日期");
        }
        if (endDate<= 0) {
            throw new IllegalArgumentException("保险合同必须有失效日期");
        }
        if (endDate<= beginDate) {
            throw new IllegalArgumentException("保险合同失效日期必须大于生效日期");
        }

        return new Insurancecontract(this);
    }
  1. Further, the build object and the built object are merged.
    Incorporate the builder into the contract object as an inner class.
/**
 * 保险合同对象
 *
 * @author lijun
 * @since 2018-03-29 13:18
 */
public class InsurancecontractMergeBuilder {

    /**
     * 保险合同编号
     */
    private String contractId;

    /**
     * 被保人名称 同一份保险合同,要么跟人员签订,要么跟公司签订,也就是说,“被保险人员”和“被保公司"
     * 这个两个属性不可能同时有值。
     */
    private String personName;

    /**
     * 被保公司
     */
    private String companyName;

    /**
     * 保险开始生效的时间
     */
    private long beginDate;

    /**
     * beginDate< endDate
     * 保险失效时间
     */
    private long endDate;


    /**
     * 其他数据
     */
    private String otherData;


   private   InsurancecontractMergeBuilder(ConcreteBuilder concreteBuilder) {
       this.contractId = concreteBuilder.getContractId();
       this.beginDate = concreteBuilder.getBeginDate();
       this.companyName = concreteBuilder.getCompanyName();
       this.endDate = concreteBuilder.getEndDate();
       this.otherData = concreteBuilder.getOtherData();
       this.personName = concreteBuilder.getPersonName();
    }


    /**
     *构建保险合同对象的构建器 作为保险合同的类级内部内
     */
    public static class ConcreteBuilder {

        /**
         * 保险合同编号
         */
        private String contractId;

        /**
         * 被保人名称 同一份保险合同,要么跟人员签订,要么跟公司签订,也就是说,“被保险人员”和“被保公司"
         * 这个两个属性不可能同时有值。
         */
        private String personName;

        /**
         * 被保公司
         */
        private String companyName;

        /**
         * 保险开始生效的时间
         */
        private long beginDate;

        /**
         * 保险失效时间
         */
        private long endDate;


        /**
         * 其他数据
         */
        private String otherData;

        public ConcreteBuilder(String contractId, long beginDate, long endDate) {
            this.contractId = contractId;
            this.beginDate = beginDate;
            this.endDate = endDate;
        }

        public ConcreteBuilder setPersonName(String personName) {
            this.personName = personName;
            return this;
        }

        public ConcreteBuilder setCompanyName(String companyName) {
            this.companyName = companyName;
            return this;
        }

        public ConcreteBuilder setOtherData(String otherData) {
            this.otherData = otherData;
            return this;
        }

        public String getContractId() {
            return contractId;
        }

        public void setContractId(String contractId) {
            this.contractId = contractId;
        }

        public String getPersonName() {
            return personName;
        }


        public String getCompanyName() {
            return companyName;
        }


        public long getBeginDate() {
            return beginDate;
        }

        public void setBeginDate(long beginDate) {
            this.beginDate = beginDate;
        }

        public long getEndDate() {
            return endDate;
        }

        public void setEndDate(long endDate) {
            this.endDate = endDate;
        }

        public String getOtherData() {
            return otherData;
        }

        /**
         * 构建真正的对象并返回
         *
         * @return 构建的保险合同对象
         */
        public InsurancecontractMergeBuilder build() {
            if (contractId == null || contractId.trim().length() == 0) {
                throw new IllegalArgumentException("合同编号不能为空!");
            }
            boolean personIs = personName == null || personName.trim().length() == 0;
            boolean companyIs = companyName == null || companyName.trim().length() == 0;

            if (personIs && companyIs) {
                throw new IllegalArgumentException("一份合同不能同时和个人和公司同签!");
            }

            if (personIs == false && companyIs == false) {
                throw new IllegalArgumentException("一份合同不能没有签订对象");
            }

            if (beginDate <= 0) {
                throw new IllegalArgumentException("保险合同必须有开始日期");
            }
            if (endDate<= 0) {
                throw new IllegalArgumentException("保险合同必须有失效日期");
            }
            if (endDate<= beginDate) {
                throw new IllegalArgumentException("保险合同失效日期必须大于生效日期");
            }

            return new InsurancecontractMergeBuilder(this);
        }

    }


    /**
     * 保险合同的某些操作
     */
    public void someOperation() {
        System.out.println("一些操作 " + this.contractId);
    }
}

ClientMergeBuilder

public class ClientMergeBuilder {
    public static void main(String[] args) {
        InsurancecontractMergeBuilder.ConcreteBuilder concreteBuilder = new InsurancecontractMergeBuilder
                .ConcreteBuilder("2000", 1234L, 4567L);
        InsurancecontractMergeBuilder insurancecontractMergeBuilder = concreteBuilder.setCompanyName("X")
                .setOtherData("OF").build();
        insurancecontractMergeBuilder.someOperation();
    }
}

4. Essence

Separating the overall build algorithm and component construction.

5. Summary

  • advantage
    • The loosely coupled
      generator pattern can use the same build algorithm to build products with completely different performances, and realize the separation of product construction and product performance. The generator pattern is to separate the process of product construction, so that it is loosely coupled with the performance of specific products, so that the construction algorithm can be reused, and the performance of specific products can be expanded and switched flexibly and easily.
    • The internal representation of the product can be easily changed.
      In the Builder pattern, since the Builder object only provides an interface for the Director to use, the specific component creation and assembly methods are hidden by the Builder interface, and the Director does not know these specific implementation details. In this way, if you want to change the internal representation of the product, you only need to switch the specific implementation of the Builder interface, regardless of the Director, so it becomes very easy.
    • Better reusability The
      generator pattern is a good way to separate the build algorithm from the specific product implementation. In this way, the algorithm for building the product can be reused. In the same way, the implementation of specific products can also be reused, and the implementation of the same product can be used with different construction algorithms.

6. Reference

This article refers to the "grinding design pattern"

Source address builder-pattern

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325582031&siteId=291194637