一天一模式之1生成器模式

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表现形式。

Builder

生成器接口,定义创建一个Product对象所需的各个部件的操作。

ConcreteBuilder:

具体的生成器实现,实现各个部件的创建,并负责组装Product对象的各个
部件,同时还提供一个让用户获取组装完成后的产品对象的方法。

Director:

指导者,也被称为导向者,主要用来使用Builder接口,以一个统一的过程
来构建所需要的Product对象。

Product:

产品,表示被生成器构建的复杂对象,包好多个部件。

示例

package cn.javass.dp.builder.example2;

/**
 * 构建器接口,定义创建一个产品对象所需的各个部件的操作
 */
public interface Builder {
    /**
     * 示意方法,构建某个部件
     */
    public void buildPart();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
具体的构建器实现对象
package cn.javass.dp.builder.example2;
/**
 * 具体的构建器实现对象
 */
public class ConcreteBuilder implements Builder {
    /**
     * 构建器最终构建的产品对象
     */
    private Product resultProduct;
    /**
     * 获取构建器最终构建的产品对象
     * @return 构建器最终构建的产品对象
     */
    public Product getResult() {
        return resultProduct;
    }
    public void buildPart() {
        //构建某个部件的功能处理
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
指导者,指导使用构建器的接口来构建产品的对象
package cn.javass.dp.builder.example2;
/**
 * 指导者,指导使用构建器的接口来构建产品的对象
 */
public class Director {
    /**
     * 持有当前需要使用的构建器对象
     */
    private Builder builder;
    /**
     * 构造方法,传入构建器对象
     * @param builder 构建器对象
     */
    public Director(Builder builder) {
        this.builder = builder;
    }
    /**
     * 示意方法,指导构建器构建最终的产品对象
     */
    public void construct() {
        //通过使用构建器接口来构建最终的产品对象
        builder.buildPart();
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
被构建的产品对象的接口
package cn.javass.dp.builder.example2;
/**
 * 被构建的产品对象的接口
 */
public interface Product {
    //定义产品的操作
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

导出数据的应用架构

对于导出数据的应用框架,通常对于具体的导出内容和格式是有要求的,
- (1)导出的文件,不管什么格式,都分成三个部分,分别是文件头、文件体和文件
- (2)在文件头部分,需要描述如下信息:分公司或门市点编号、导出数据的日期,
- (3)在文件体部分,需要描述如下信息:表名称、然后分条描述数据。对于文本格
式,表名称单独占一行,数据描述一行算一条数据,字段间用逗号分隔。
- (4)在文件尾部分,需要描述如下信息:输出人
现在就要来实现上述功能。为了演示简单点,在工厂方法模式里面已经实
现的功能,这里就不去重复了,这里只关心如何实现导出文件,而且只实现导出
成文本格式和XML格式就可以了,其它的就不去考虑了。

不用模式的解决方案

示例
描述输出数据的对象
package cn.javass.dp.builder.example1;
/**
 * 描述输出数据的对象
 */
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;
    }

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
描述输出到文件头的内容的对象
package cn.javass.dp.builder.example1;
/**
 * 描述输出到文件头的内容的对象
 */
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;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
描述输出到文件尾的内容的对象
package cn.javass.dp.builder.example1;
/**
 * 描述输出到文件尾的内容的对象
 */
public class ExportFooterModel {
    /**
     * 输出人
     */
    private String exportUser;

    public String getExportUser() {
        return exportUser;
    }

    public void setExportUser(String exportUser) {
        this.exportUser = exportUser;
    }

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
导出数据到文本文件的对象
package cn.javass.dp.builder.example1;
import java.util.*;
/**
 * 导出数据到文本文件的对象
 */
public class ExportToTxt {
    /**
     * 导出数据到文本文件
     * @param ehm 文件头的内容
     * @param mapData 数据的内容
     * @param efm 文件尾的内容
     */
    public void export(ExportHeaderModel ehm,Map<String,Collection<ExportDataModel>> mapData,ExportFooterModel efm){
        //用来记录最终输出的文件内容
        StringBuffer buffer = new StringBuffer();
        //1:先来拼接文件头的内容
        buffer.append(ehm.getDepId()+","+ehm.getExportDate()+"\n");
        //2:接着来拼接文件体的内容
        for(String tblName : mapData.keySet()){
            //先拼接表名称
            buffer.append(tblName+"\n");
            //然后循环拼接具体数据
            for(ExportDataModel edm : mapData.get(tblName)){
                buffer.append(edm.getProductId()+","+edm.getPrice()+","+edm.getAmount()+"\n");
            }
        }
        //3:接着来拼接文件尾的内容
        buffer.append(efm.getExportUser());

        //为了演示简洁性,这里就不去写输出文件的代码了
        //把要输出的内容输出到控制台看看
        System.out.println("输出到文本文件的内容:\n"+buffer);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
导出数据到XML文件的对象
package cn.javass.dp.builder.example1;

import java.util.Collection;
import java.util.Map;
/**
 * 导出数据到XML文件的对象
 */
public class ExportToXml {
    /**
     * 导出数据到XML文件
     * @param ehm 文件头的内容
     * @param mapData 数据的内容
     * @param efm 文件尾的内容
     */
    public void export(ExportHeaderModel ehm,Map<String,Collection<ExportDataModel>> mapData,ExportFooterModel efm){
        //用来记录最终输出的文件内容
        StringBuffer buffer = new StringBuffer();
        //1:先来拼接文件头的内容
        buffer.append("<?xml version='1.0' encoding='gb2312'?>\n");
        buffer.append("<Report>\n");
        buffer.append("  <Header>\n");
        buffer.append("    <DepId>"+ehm.getDepId()+"</DepId>\n");
        buffer.append("    <ExportDate>"+ehm.getExportDate()+"</ExportDate>\n");
        buffer.append("  </Header>\n");
        //2:接着来拼接文件体的内容
        buffer.append("  <Body>\n");
        for(String tblName : mapData.keySet()){
            //先拼接表名称
            buffer.append("    <Datas TableName=\""+tblName+"\">\n");
            //然后循环拼接具体数据
            for(ExportDataModel edm : mapData.get(tblName)){
                buffer.append("      <Data>\n");
                buffer.append("        <ProductId>"+edm.getProductId()+"</ProductId>\n");
                buffer.append("        <Price>"+edm.getPrice()+"</Price>\n");
                buffer.append("        <Amount>"+edm.getAmount()+"</Amount>\n");
                buffer.append("      </Data>\n");
            }
            buffer.append("    </Datas>\n");
        }
        buffer.append("  </Body>\n");
        //3:接着来拼接文件尾的内容
        buffer.append("  <Footer>\n");
        buffer.append("    <ExportUser>"+efm.getExportUser()+"</ExportUser>\n");
        buffer.append("  </Footer>\n");
        buffer.append("</Report>\n");

        //为了演示简洁性,这里就不去写输出文件的代码了
        //把要输出的内容输出到控制台看看
        System.out.println("输出到XML文件的内容:\n"+buffer);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
存在的问题

对于不同的输出格式,处理步骤是一样的,但是具体每步的实现是不一样的。
- (1)先拼接文件头的内容
按照现在的实现方式,就存在如下的问题:
- (1)构建每种输出格式的文件内容的时候,都会重复这几个处理步骤,应该提炼出
来,形成公共的处理过程
- (2)今后可能会有很多不同输出格式的要求,这就需要在处理过程不变的情况下,
能方便的切换不同的输出格式的处理

换句话说,也就是构建每种格式的数据文件的处理过程,应该和具体的步
骤实现分开,这样就能够复用处理过程,而且能很容易的切换不同的输出格式。

输出文件头
package cn.javass.dp.builder.example1;
/**
 * 描述输出到文件头的内容的对象
 */
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;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
输出文件体
package cn.javass.dp.builder.example1;
/**
 * 描述输出数据的对象
 */
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;
    }

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
输出文件尾
package cn.javass.dp.builder.example1;
/**
 * 描述输出到文件尾的内容的对象
 */
public class ExportFooterModel {
    /**
     * 输出人
     */
    private String exportUser;

    public String getExportUser() {
        return exportUser;
    }

    public void setExportUser(String exportUser) {
        this.exportUser = exportUser;
    }

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
导入文本文件
package cn.javass.dp.builder.example1;
import java.util.*;
/**
 * 导出数据到文本文件的对象
 */
public class ExportToTxt {
    /**
     * 导出数据到文本文件
     * @param ehm 文件头的内容
     * @param mapData 数据的内容
     * @param efm 文件尾的内容
     */
    public void export(ExportHeaderModel ehm,Map<String,Collection<ExportDataModel>> mapData,ExportFooterModel efm){
        //用来记录最终输出的文件内容
        StringBuffer buffer = new StringBuffer();
        //1:先来拼接文件头的内容
        buffer.append(ehm.getDepId()+","+ehm.getExportDate()+"\n");
        //2:接着来拼接文件体的内容
        for(String tblName : mapData.keySet()){
            //先拼接表名称
            buffer.append(tblName+"\n");
            //然后循环拼接具体数据
            for(ExportDataModel edm : mapData.get(tblName)){
                buffer.append(edm.getProductId()+","+edm.getPrice()+","+edm.getAmount()+"\n");
            }
        }
        //3:接着来拼接文件尾的内容
        buffer.append(efm.getExportUser());

        //为了演示简洁性,这里就不去写输出文件的代码了
        //把要输出的内容输出到控制台看看
        System.out.println("输出到文本文件的内容:\n"+buffer);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
导入xml文件
package cn.javass.dp.builder.example1;

import java.util.Collection;
import java.util.Map;
/**
 * 导出数据到XML文件的对象
 */
public class ExportToXml {
    /**
     * 导出数据到XML文件
     * @param ehm 文件头的内容
     * @param mapData 数据的内容
     * @param efm 文件尾的内容
     */
    public void export(ExportHeaderModel ehm,Map<String,Collection<ExportDataModel>> mapData,ExportFooterModel efm){
        //用来记录最终输出的文件内容
        StringBuffer buffer = new StringBuffer();
        //1:先来拼接文件头的内容
        buffer.append("<?xml version='1.0' encoding='gb2312'?>\n");
        buffer.append("<Report>\n");
        buffer.append("  <Header>\n");
        buffer.append("    <DepId>"+ehm.getDepId()+"</DepId>\n");
        buffer.append("    <ExportDate>"+ehm.getExportDate()+"</ExportDate>\n");
        buffer.append("  </Header>\n");
        //2:接着来拼接文件体的内容
        buffer.append("  <Body>\n");
        for(String tblName : mapData.keySet()){
            //先拼接表名称
            buffer.append("    <Datas TableName=\""+tblName+"\">\n");
            //然后循环拼接具体数据
            for(ExportDataModel edm : mapData.get(tblName)){
                buffer.append("      <Data>\n");
                buffer.append("        <ProductId>"+edm.getProductId()+"</ProductId>\n");
                buffer.append("        <Price>"+edm.getPrice()+"</Price>\n");
                buffer.append("        <Amount>"+edm.getAmount()+"</Amount>\n");
                buffer.append("      </Data>\n");
            }
            buffer.append("    </Datas>\n");
        }
        buffer.append("  </Body>\n");
        //3:接着来拼接文件尾的内容
        buffer.append("  <Footer>\n");
        buffer.append("    <ExportUser>"+efm.getExportUser()+"</ExportUser>\n");
        buffer.append("  </Footer>\n");
        buffer.append("</Report>\n");

        //为了演示简洁性,这里就不去写输出文件的代码了
        //把要输出的内容输出到控制台看看
        System.out.println("输出到XML文件的内容:\n"+buffer);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
客户端
package cn.javass.dp.builder.example1;

import java.util.*;

public class Client {
    public static void main(String[] args) {
        //准备测试数据
        ExportHeaderModel ehm = new ExportHeaderModel();
        ehm.setDepId("一分公司");
        ehm.setExportDate("2012-06-26");

        Map<String,Collection<ExportDataModel>> mapData = new HashMap<String,Collection<ExportDataModel>>();
        Collection<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(99);
        edm2.setAmount(55);     
        //把数据组装起来
        col.add(edm1);
        col.add(edm2);      
        mapData.put("销售记录表", col);

        ExportFooterModel efm = new ExportFooterModel();
        efm.setExportUser("张三");

        //测试输出到文本文件
        ExportToTxt toTxt = new ExportToTxt();
        toTxt.export(ehm, mapData, efm);
        //测试输出到xml文件
        ExportToXml toXml = new ExportToXml();
        toXml.export(ehm, mapData, efm);

    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

理解生成器模式

认识生成器模式

1:生成器模式的功能

生成器模式的主要功能是构建复杂的产品,而且是细化的,分步骤的构建
产品,也就是生成器模式重在解决一步一步构造复杂对象的问题。如果光是这么
认识生成器模式的功能是不够的。

更为重要的是,**这个构建的过程是统一的,固定不变的,变化的部分放到
生成器部分了,**只要配置不同的生成器,那么同样的构建过程,就能构建出不同
的产品表示来。

直白点说,生成器模式的重心在于分离构建算法和具体的构造实现,从而
使得构建算法可以重用,具体的构造实现可以很方便的扩展和切换,从而可以灵
活的组合来构造出不同的产品对象。

2:生成器模式的构成

要特别注意,生成器模式分成两个很重要的部分:
- (1)一个部分是Builder接口这边,这边是定义了如何构建各个部件,也就是知道
每个部件功能如何实现,以及如何装配这些部件到产品中去;
- (2)另外一个部分是Director这边,Director是知道如何组合来构建产品,也就是
说Director负责整体的构建算法,而且通常是分步骤的来执行。

不管如何变化,Builder模式都存在这么两个部分,一个部分是部件构造和
产品装配,另一个部分是整体构建的算法。认识这点是很重要的,因为在生成器
模式中,强调的是固定整体构建的算法,而灵活扩展和切换部件的具体构造和产
品装配的方式,所以要严格区分这两个部分。

在Director实现整体构建算法的时候,遇到需要创建和组合具体部件的时
候,就会把这些功能通过委托,交给Builder去完成。

3:生成器模式的使用

应用生成器模式的时候,可以让客户端创造Director,在Director里面封
装整体构建算法,然后让Director去调用Builder,让Builder来封装具体部件的
构建功能,这就跟前面的例子一样。

还有一种退化的情况,就是让客户端和Director融合起来,让客户端直接
去操作Builder,就好像是指导者自己想要给自己构建产品一样。

生成器模式样板

构建器

package cn.javass.dp.builder.example2;

/**
 * 构建器接口,定义创建一个产品对象所需的各个部件的操作
 */
public interface Builder {
    /**
     * 示意方法,构建某个部件
     */
    public void buildPart();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

构建器实现对象

package cn.javass.dp.builder.example2;
/**
 * 具体的构建器实现对象
 */
public class ConcreteBuilder implements Builder {
    /**
     * 构建器最终构建的产品对象
     */
    private Product resultProduct;
    /**
     * 获取构建器最终构建的产品对象
     * @return 构建器最终构建的产品对象
     */
    public Product getResult() {
        return resultProduct;
    }
    public void buildPart() {
        //构建某个部件的功能处理
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

指导者

package cn.javass.dp.builder.example2;
/**
 * 指导者,指导使用构建器的接口来构建产品的对象
 */
public class Director {
    /**
     * 持有当前需要使用的构建器对象
     */
    private Builder builder;
    /**
     * 构造方法,传入构建器对象
     * @param builder 构建器对象
     */
    public Director(Builder builder) {
        this.builder = builder;
    }
    /**
     * 示意方法,指导构建器构建最终的产品对象
     */
    public void construct() {
        //通过使用构建器接口来构建最终的产品对象
        builder.buildPart();
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

产品对象

package cn.javass.dp.builder.example2;
/**
 * 被构建的产品对象的接口
 */
public interface Product {
    //定义产品的操作
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

构造器实现文件导出

构造器接口

package cn.javass.dp.builder.example3;

import java.util.Collection;
import java.util.Map;

/**
 * 构建器接口,定义创建一个输出文件对象所需的各个部件的操作
 */
public interface Builder {
    /**
     * 构建输出文件的Header部分
     * @param ehm 文件头的内容
     */
    public void buildHeader(ExportHeaderModel ehm);
    /**
     * 构建输出文件的Body部分
     * @param mapData 要输出的数据的内容
     */
    public void buildBody(Map<String,Collection<ExportDataModel>> mapData);
    /**
     * 构建输出文件的Footer部分
     * @param efm 文件尾的内容
     */
    public void buildFooter(ExportFooterModel efm);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

实现导出数据到文本文件的的构建器对象

package cn.javass.dp.builder.example3;

import java.util.Collection;
import java.util.Map;

/**
 * 实现导出数据到文本文件的的构建器对象
 */
public class TxtBuilder implements Builder {
    /**
     * 用来记录构建的文件的内容,相当于产品
     */
    private StringBuffer buffer = new StringBuffer();
    public void buildBody(
            Map<String, Collection<ExportDataModel>> mapData) {
        for(String tblName : mapData.keySet()){
            //先拼接表名称
            buffer.append(tblName+"\n");
            //然后循环拼接具体数据
            for(ExportDataModel edm : mapData.get(tblName)){
                buffer.append(edm.getProductId()+","+edm.getPrice()+","+edm.getAmount()+"\n");
            }
        }
    }
    public void buildFooter(ExportFooterModel efm) {
        buffer.append(efm.getExportUser());
    }
    public void buildHeader(ExportHeaderModel ehm) {
        buffer.append(ehm.getDepId()+","+ehm.getExportDate()+"\n");
    }   
    public StringBuffer getResult(){
        return buffer;
    }   
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

实现导出数据到XML文件的的构建器对象

package cn.javass.dp.builder.example3;

import java.util.Collection;
import java.util.Map;

/**
 * 实现导出数据到XML文件的的构建器对象
 */
public class XmlBuilder implements Builder {
    /**
     * 用来记录构建的文件的内容,相当于产品
     */
    private StringBuffer buffer = new StringBuffer();
    public void buildBody(
            Map<String, Collection<ExportDataModel>> mapData) {
        buffer.append("  <Body>\n");
        for(String tblName : mapData.keySet()){
            //先拼接表名称
            buffer.append("    <Datas TableName=\""+tblName+"\">\n");
            //然后循环拼接具体数据
            for(ExportDataModel edm : mapData.get(tblName)){
                buffer.append("      <Data>\n");
                buffer.append("        <ProductId>"+edm.getProductId()+"</ProductId>\n");
                buffer.append("        <Price>"+edm.getPrice()+"</Price>\n");
                buffer.append("        <Amount>"+edm.getAmount()+"</Amount>\n");
                buffer.append("      </Data>\n");
            }
            buffer.append("    </Datas>\n");
        }
        buffer.append("  </Body>\n");
    }

    public void buildFooter(ExportFooterModel efm) {
        //对象的创建过程

        //不是由自己来创建对象,而是使用其它组件创建的对象
        //比如:简单工厂、工厂方法
        MyFooter mf = FooterFactory.createMyFooter();

        //组件组装过程
        buffer.append(mf.genHeader(efm));
    }

    public void buildHeader(ExportHeaderModel ehm) {
        buffer.append("<?xml version='1.0' encoding='gb2312'?>\n");
        buffer.append("<Report>\n");
        buffer.append("  <Header>\n");
        buffer.append("    <DepId>"+ehm.getDepId()+"</DepId>\n");
        buffer.append("    <ExportDate>"+ehm.getExportDate()+"</ExportDate>\n");
        buffer.append("  </Header>\n");
    }
    public StringBuffer getResult(){
        return buffer;
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

利用工厂创建组件对象

文档对象尾
package cn.javass.dp.builder.example3;

public class MyFooter {

    public String genHeader(ExportFooterModel efm){
        String str = "  <Footer>\n";
        str+="    <ExportUser>"+efm.getExportUser()+"</ExportUser>\n";
        str+="  </Footer>\n";
        str+="</Report>\n";
        return str;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
文档创建对象工厂
package cn.javass.dp.builder.example3;

public class FooterFactory {
    public static MyFooter createMyFooter(){
        return new MyFooter();
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

指导者

package cn.javass.dp.builder.example3;

import java.util.Collection;
import java.util.Map;

/**
 * 指导者,指导使用构建器的接口来构建输出的文件的对象
 */
public class Director {
    /**
     * 持有当前需要使用的构建器对象
     */
    private Builder builder;
    /**
     * 构造方法,传入构建器对象
     * @param builder 构建器对象
     */
    public Director(Builder builder) {
        this.builder = builder;
    }
    /**
     * 指导构建器构建最终的输出的文件的对象
     * @param ehm 文件头的内容
     * @param mapData 数据的内容
     * @param efm 文件尾的内容
     */
    public void construct(ExportHeaderModel ehm,Map<String,Collection<ExportDataModel>> mapData,ExportFooterModel efm) {
        //进行数据校验

        //1:先构建Header
        builder.buildHeader(ehm);
        //实现一个Header的后处理
        //可以在这里实现一些业务
        //2:然后构建Body
        builder.buildBody(mapData);
        //3:然后构建Footer
        builder.buildFooter(efm);
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

客户端

package cn.javass.dp.builder.example3;

import java.util.*;

public class Client {
    public static void main(String[] args) {
        //准备测试数据
        ExportHeaderModel ehm = new ExportHeaderModel();
        ehm.setDepId("一分公司");
        ehm.setExportDate("2010-05-18");

        Map<String,Collection<ExportDataModel>> mapData = new HashMap<String,Collection<ExportDataModel>>();
        Collection<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(99);
        edm2.setAmount(55);     
        //把数据组装起来
        col.add(edm1);
        col.add(edm2);      
        mapData.put("销售记录表", col);

        ExportFooterModel efm = new ExportFooterModel();
        efm.setExportUser("张三");

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

        director.construct(ehm, mapData, efm);
        //把要输出的内容输出到控制台看看
        System.out.println("输出到文本文件的内容:\n"+txtBuilder.getResult());


        //测试输出到xml文件
        XmlBuilder xmlBuilder = new XmlBuilder();

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

        xmlBuilder.buildHeader(ehm);
        xmlBuilder.buildBody(mapData);
        xmlBuilder.buildFooter(efm);

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

    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

生成器模式的实现

(1)生成器的实现

实际上在Builder接口的实现中,每个部件构建的方法里面,除了部件装配
外,也可以实现如何具体的创建各个部件对象,也就是说每个方法都可以有两部
分功能,一个是创建部件对象,一个是组装部件。

在构建部件的方法里面可以实现选择并创建具体的部件对象,然后再把这
个部件对象组装到产品对象中去,这样一来,Builder就可以和工厂方法配合使
用了。

再进一步,如果在实现Builder的时候,只有创建对象的功能,而没有组装
的功能,那么这个时候的Builder实现跟抽象工厂的实现是类似的。

这种情况下,Builder接口就类似于抽象工厂的接口,Builder的具体实现
就类似于具体的工厂,而且Builder接口里面定义的创建各个部件的方法也是有
关联的,这些方法是构建一个复杂对象所需要的部件对象,仔细想想,是不是非
常类似呢。

(2)指导者的实现

在生成器模式里面,指导者承担的是整体构建算法部分,是相对不变的部
分。因此在实现指导者的时候,把变化的部分分离出去是很重要的。

其实指导者分离出去的变化部分,就到了生成器那边,指导者知道整体的
构建算法,就是不知道如何具体的创建和装配部件对象。

因此真正的指导者实现,并不仅仅是如同前面示例那样,简单的按照一定
顺序调用生成器的方法来生成对象,并没有这么简单。应该是有较为复杂的算法
和运算过程,在运算过程中根据需要,才会调用生成器的方法来生成部件对象。

(3)指导者和生成器的交互

在生成器模式里面,指导者和生成器的交互,是通过生成器的那些
buildPart方法来完成的。指导者通常会实现比较复杂的算法或者是运算过程,
在实际中很可能会有这样的情况:
- a:在运行指导者的时候,会按照整体构建算法的步骤进行运算,可能先运行前几步
运算,到了某一步骤,需要具体创建某个部件对象了,然后就调用Builder中创
建相应部件的方法来创建具体的部件。同时,把前面运算得到的数据传递给
Builder,因为在Builder内部实现创建和组装部件的时候,可能会需要这些数据
- b:Builder创建完具体的部件对象后,会把创建好的部件对象返回给指导者,指导
者继续后续的算法运算,可能会用到已经创建好的对象
- c:如此反复下去,直到整个构建算法运行完成,那么最终的产品对象也就创建好了
通过上面的描述,可以看出指导者和生成器是需要交互的,方式就是通过
生成器方法的参数和返回值,来回的传递数据。事实上,指导者是通过委托的方
式来把功能交给生成器去完成。

(4)返回装配好的产品方法

在标准的生成器模式里面,在Builder实现里面会提供一个返回装配好的产
品的方法,在Builder接口上是没有的。它考虑的是最终的对象一定要通过部件
构建和装配,才算真正创建了,而具体干活的就是这个Builder实现,虽然指导
者也参与了,但是指导者是不负责具体的部件创建和组装的,因此客户端是从
Builder实现里面获取最终装配好的产品。

(5)关于被创建的产品接口

在使用生成器模式的时候,大多数情况下是不知道最终构建出来的产品是
什么样的,所以在标准的生成器模式里面,一般是不需要对产品定义抽象接口
的,因为最终构造的产品千差万别,给这些产品定义公共接口几乎是没有意义
的。

使用生成器模式构建复杂对象

考虑这样一个实际应用,要创建一个保险合同的对象,里面很多属性的值
都有约束,要求创建出来的对象是满足这些约束规则的。约束规则比如:保险合
同通常情况下可以和个人签订,也可以和某个公司签订,但是一份保险合同不能
同时与个人和公司签订。这个对象里面有很多类似这样的约束,那么该如何来创
建这个对象呢?

1:使用Builder模式构建复杂对象,先不考虑约束

保险合同的对象
package cn.javass.dp.builder.example4;
/**
 * 保险合同的对象
 */
public class InsuranceContract {
    /**
     * 保险合同编号
     */
    private String contractId;
    /**
     * 被保险人员的名称,同一份保险合同,要么跟人员签订,要么跟公司签订,
     * 也就是说,"被保险人员"和"被保险公司"这两个属性,不可能同时有值
     */
    private String personName;
    /**
     * 被保险公司的名称
     */
    private String companyName;
    /**
     * 保险开始生效的日期
     */
    private long beginDate;
    /**
     * 保险失效的日期,一定会大于保险开始生效的日期
     */
    private long endDate;
    /**
     * 示例:其它数据
     */
    private String otherData;

    /**
     * 构造方法,访问级别是同包能访问
     */
    InsuranceContract(ConcreteBuilder builder){
        this.contractId = builder.getContractId();
        this.personName = builder.getPersonName();
        this.companyName = builder.getCompanyName();
        this.beginDate = builder.getBeginDate();
        this.endDate = builder.getEndDate();
        this.otherData = builder.getOtherData();
    }

    /**
     * 示意:保险合同的某些操作
     */
    public void someOperation(){
        System.out.println("Now in Insurance Contract someOperation=="+this.contractId+",personName="+this.personName+" , cmpanyName="+this.companyName);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
构造保险合同对象的构建器
package cn.javass.dp.builder.example4;
/**
 * 构造保险合同对象的构建器
 */
public class ConcreteBuilder {
    private String contractId;
    private String personName;
    private String companyName;
    private long beginDate;
    private long endDate;
    private String otherData;
    /**
     * 构造方法,传入必须要有的参数
     * @param contractId 保险合同编号
     * @param beginDate 保险开始生效的日期
     * @param endDate 保险失效的日期
     */
    public ConcreteBuilder(String contractId,long beginDate,long endDate){
        this.contractId = contractId;
        this.beginDate = beginDate;
        this.endDate = endDate;
    }
    /**
     * 选填数据,被保险人员的名称
     * @param personName  被保险人员的名称
     * @return 构建器对象
     */
    public ConcreteBuilder setPersonName(String personName){
        this.personName = personName;
        return this;
    }
    /**
     *  选填数据,被保险公司的名称
     * @param companyName 被保险公司的名称
     * @return 构建器对象
     */
    public ConcreteBuilder setCompanyName(String companyName){
        this.companyName = companyName;
        return this;
    }
    /**
     * 选填数据,其它数据
     * @param otherData 其它数据
     * @return 构建器对象
     */
    public ConcreteBuilder setOtherData(String otherData){
        this.otherData = otherData;
        return this;
    }
    /**
     * 构建真正的对象并返回
     * @return 构建的保险合同的对象
     */
    public InsuranceContract build(){
        return new InsuranceContract(this);
    }

    public String getContractId() {
        return contractId;
    }

    public String getPersonName() {
        return personName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public long getBeginDate() {
        return beginDate;
    }

    public long getEndDate() {
        return endDate;
    }

    public String getOtherData() {
        return otherData;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
客户端
package cn.javass.dp.builder.example4;

public class Client {
    public static void main(String[] args) {
        //创建构建器
        ConcreteBuilder builder = new ConcreteBuilder("001",12345L,67890L);
        //设置需要的数据,然后构建保险合同对象
        InsuranceContract contract = 
                builder.setPersonName("张三")
                .setOtherData("test")
                .build();

        //操作保险合同对象的方法
        contract.someOperation();

        ConcreteBuilder builder2 = new ConcreteBuilder("001",12345L,67890L);
        InsuranceContract contract2 = 
                builder2
                    .setPersonName("李四")
                    .setCompanyName("cc")
                    .setOtherData("company")
                    .build();
        contract2.someOperation();


    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2:使用Builder模式构建复杂对象,考虑带约束规则

构造保险合同对象的构建器
package cn.javass.dp.builder.example5;
/**
 * 构造保险合同对象的构建器
 */
public class ConcreteBuilder {
    private String contractId;
    private String personName;
    private String companyName;
    private long beginDate;
    private long endDate;
    private String otherData;
    /**
     * 构造方法,传入必须要有的参数
     * @param contractId 保险合同编号
     * @param beginDate 保险开始生效的日期
     * @param endDate 保险失效的日期
     */
    public ConcreteBuilder(String contractId,long beginDate,long endDate){
        this.contractId = contractId;
        this.beginDate = beginDate;
        this.endDate = endDate;
    }
    /**
     * 选填数据,被保险人员的名称
     * @param personName  被保险人员的名称
     * @return 构建器对象
     */
    public ConcreteBuilder setPersonName(String personName){
        this.personName = personName;
        return this;
    }
    /**
     *  选填数据,被保险公司的名称
     * @param companyName 被保险公司的名称
     * @return 构建器对象
     */
    public ConcreteBuilder setCompanyName(String companyName){
        this.companyName = companyName;
        return this;
    }
    /**
     * 选填数据,其它数据
     * @param otherData 其它数据
     * @return 构建器对象
     */
    public ConcreteBuilder setOtherData(String otherData){
        this.otherData = otherData;
        return this;
    }
    /**
     * 构建真正的对象并返回
     * @return 构建的保险合同的对象
     */
    public InsuranceContract build(){
        if(contractId==null || contractId.trim().length()==0){
            throw new IllegalArgumentException("合同编号不能为空");
        }
        boolean signPerson = personName!=null && personName.trim().length()>0;
        boolean signCompany = companyName!=null && companyName.trim().length()>0;
        if(signPerson && signCompany){
            throw new IllegalArgumentException("一份保险合同不能同时与人和公司签订");
        }       
        if(signPerson==false && signCompany==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);
    }

    public String getContractId() {
        return contractId;
    }

    public String getPersonName() {
        return personName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public long getBeginDate() {
        return beginDate;
    }

    public long getEndDate() {
        return endDate;
    }

    public String getOtherData() {
        return otherData;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
保险合同的对象
package cn.javass.dp.builder.example5;
/**
 * 保险合同的对象
 */
public class InsuranceContract {
    /**
     * 保险合同编号
     */
    private String contractId;
    /**
     * 被保险人员的名称,同一份保险合同,要么跟人员签订,要么跟公司签订,
     * 也就是说,"被保险人员"和"被保险公司"这两个属性,不可能同时有值
     */
    private String personName;
    /**
     * 被保险公司的名称
     */
    private String companyName;
    /**
     * 保险开始生效的日期
     */
    private long beginDate;
    /**
     * 保险失效的日期,一定会大于保险开始生效的日期
     */
    private long endDate;
    /**
     * 示例:其它数据
     */
    private String otherData;

    /**
     * 构造方法,访问级别是同包能访问
     */
    InsuranceContract(ConcreteBuilder builder){
        this.contractId = builder.getContractId();
        this.personName = builder.getPersonName();
        this.companyName = builder.getCompanyName();
        this.beginDate = builder.getBeginDate();
        this.endDate = builder.getEndDate();
        this.otherData = builder.getOtherData();
    }

    /**
     * 示意:保险合同的某些操作
     */
    public void someOperation(){
        System.out.println("Now in Insurance Contract someOperation=="+this.contractId+", personName="+personName);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
客户端
package cn.javass.dp.builder.example5;

public class Client {
    public static void main(String[] args) {
        //创建构建器
        ConcreteBuilder builder = new ConcreteBuilder("001",12345L,67890L);
        //设置需要的数据,然后构建保险合同对象
        InsuranceContract contract = builder
                .setOtherData("test")
                .setCompanyName("cc")
                .build();

        //操作保险合同对象的方法
        contract.someOperation();
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3:进一步,把构造器对象和被构造对象合并

保险合同的对象
package cn.javass.dp.builder.example6;
/**
 * 保险合同的对象
 */
public class InsuranceContract {
    /**
     * 保险合同编号
     */
    private String contractId;
    /**
     * 被保险人员的名称,同一份保险合同,要么跟人员签订,要么跟公司签订,
     * 也就是说,"被保险人员"和"被保险公司"这两个属性,不可能同时有值
     */
    private String personName;
    /**
     * 被保险公司的名称
     */
    private String companyName;
    /**
     * 保险开始生效的日期
     */
    private long beginDate;
    /**
     * 保险失效的日期,一定会大于保险开始生效的日期
     */
    private long endDate;
    /**
     * 示例:其它数据
     */
    private String otherData;

    /**
     * 构造方法,访问级别是私有的
     */
    private InsuranceContract(ConcreteBuilder builder){
        this.contractId = builder.contractId;
        this.personName = builder.personName;
        this.companyName = builder.companyName;
        this.beginDate = builder.beginDate;
        this.endDate = builder.endDate;
        this.otherData = builder.otherData;
    }

    /**
     * 构造保险合同对象的构建器
     */
    public static class ConcreteBuilder {
        private String contractId;
        private String personName;
        private String companyName;
        private long beginDate;
        private long endDate;
        private String otherData;
        /**
         * 构造方法,传入必须要有的参数
         * @param contractId 保险合同编号
         * @param beginDate 保险开始生效的日期
         * @param endDate 保险失效的日期
         */
        public ConcreteBuilder(String contractId,long beginDate,long endDate){
            this.contractId = contractId;
            this.beginDate = beginDate;
            this.endDate = endDate;
        }
        /**
         * 选填数据,被保险人员的名称
         * @param personName  被保险人员的名称
         * @return 构建器对象
         */
        public ConcreteBuilder setPersonName(String personName){
            this.personName = personName;
            return this;
        }
        /**
         *  选填数据,被保险公司的名称
         * @param companyName 被保险公司的名称
         * @return 构建器对象
         */
        public ConcreteBuilder setCompanyName(String companyName){
            this.companyName = companyName;
            return this;
        }
        /**
         * 选填数据,其它数据
         * @param otherData 其它数据
         * @return 构建器对象
         */
        public ConcreteBuilder setOtherData(String otherData){
            this.otherData = otherData;
            return this;
        }
        /**
         * 构建真正的对象并返回
         * @return 构建的保险合同的对象
         */
        public InsuranceContract build(){
            if(contractId==null || contractId.trim().length()==0){
                throw new IllegalArgumentException("合同编号不能为空");
            }
            boolean signPerson = personName!=null && personName.trim().length()>0;
            boolean signCompany = companyName!=null && companyName.trim().length()>0;
            if(signPerson && signCompany){
                throw new IllegalArgumentException("一份保险合同不能同时与人和公司签订");
            }       
            if(signPerson==false && signCompany==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);
        }
    }

    /**
     * 示意:保险合同的某些操作
     */
    public void someOperation(){
        System.out.println("Now in Insurance Contract someOperation=="+this.contractId);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
客户端
package cn.javass.dp.builder.example6;

public class Client {
    public static void main(String[] args) {
        //创建构建器
        InsuranceContract.ConcreteBuilder builder = new InsuranceContract.ConcreteBuilder("001",12345L,67890L);
        //设置需要的数据,然后构建保险合同对象
        InsuranceContract contract =            
                builder
                .setPersonName("张三")
                .setOtherData("test")
                .build();
        //操作保险合同对象的方法
        contract.someOperation();
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

生成器模式的优缺点

  1. 松散耦合
  2. 可以很容易改变产品的内部表示
  3. 更好的易用性

思考生成器模式

生成器模式的本质

生成器模式的本质是:分离整体构建算法和部件构造

何时选用生成器模式

  1. 如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时
  2. 如果同一个构建过程有着不同的表示时
# 欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Famiglistimott

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值