15. Builder模式

15 Builder模式

  1. 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
  2. 解读
  3. 构建过程:Director,指导装配过程,但是不负责每步具体的实现,由Builder对象具体实现每步,真正的指导者实现应该是有较为复杂的算法和运算过程,在运算过程中根据需要,才会调用生成器的方法来生成部件对象,并不仅仅是如同示例那样,简单的按照一定顺序调用生成器的方法来生成对象
  4. 表示:Product,不同的表示就是属性值不同的Product
  5. 就是将构建过程单独抽象成一个Director类,然后该过程中涉及到的具体实现由Builder完成,这样Director中不直接使用Product对象,完成了Director和Product的解耦,从而使同样的构建过程,只要配置不同的Builder实现,就可以创建不同的表示
  6. 本质:分离构建的过程(Director)和构建过程中具体的实现(Builder),从而使得构建算法可以重用
  7. 将构建过程和具体实现分离,实际上也就将构建过程和表示分离了
  8. 优点
  9. 松散耦合:生成器模式可以用同一个构建算法(Director),构建出表现上完全不同的产品(Product),实现产品构建和产品表现上的分离,从而使得构建算法可以复用,而具体产品表现也可以灵活的、方便的扩展和切换
  10. 可以很容易的改变产品的内部表示:由于Builder对象只是提供接口给Director使用,那么具体的部件创建和装配方式是被Builder接口隐藏了的,Director并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换Builder的具体实现即可,不用管Director,因此变得很容易
  11. 更好的复用性:生成器模式很好的实现了构建算法和具体产品实现的分离,这样一来,使得构建产品的算法(Director)可以复用。同样的道理,具体产品的实现(Builder)也可以复用,同一个产品的实现,可以配合不同的构建算法使用。
  12. 使用场景
  13. 如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时
  14. 如果同一个构建过程有着不同的表示时,例如effective java中的示例,构建过程都是Builder.build,但当Builder对象不同时,创建出的NutritionFactsBuilder的表示也不同
  15. 类图
    在这里插入图片描述
15.1 代码
  1. Builder

    /**
     * 生成器接口,定义创建一个输出文件对象所需的各个部件的操作
     */
    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);
    }
    
  2. TxtBuilder

    /**
     * 实现导出数据到文本文件的的生成器对象
     */
    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;
        }   
    }
    
  3. XmlBuilder

    /**
     * 实现导出数据到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) {
           buffer.append("  <Footer>\n");
           buffer.append("    <ExportUser>"+efm.getExportUser()+"</ExportUser>\n");
           buffer.append("  </Footer>\n");
           buffer.append("</Report>\n");
        }
        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;
        }
    }
    
  4. Director

    /**
     * 指导者,指导使用生成器的接口来构建输出的文件的对象
     */
    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);
           //2:然后构建Body
           builder.buildBody(mapData);
           //3:然后构建Footer
           builder.buildFooter(efm);
        }
    }
    
  5. Client

    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);
           //把要输出的内容输出到控制台看看
           System.out.println("输出到XML文件的内容:\n"+xmlBuilder.getResult());
        }
    }
    
15.3 effective java中的举例
  1. 客户端相当于充当了Director的角色,来指导构建器类去构建需要的复杂对象

  2. NutritionFactsBuilder相当于Product,Builder相当于ConcreteBuilder

  3. 通过Builder的set方法可以得到一个新的Builder,使用新的Builder和老Builder创建处的Product的表现不同

  4. 为防止外界可以直接new NutritionFactsBuilder,所以将其构造器私有,且由于Builder只用于创建NutritionFactsBuilder,因此将Builder放在NutritionFactsBuilder内部

  5. NutritionFactsBuilder

    package chapter1.number2;
    //NutritionFactsBuilder为真正的产品,由于Builder只负责创建这一种产品,所以可以将Builder作为其内部类
    public class NutritionFactsBuilder {
        //因为想构建不可变类,所以所有属性都设置为final
    	private final int servingSize;//分量大小 必须
    	private final int servings;//使用次数 必须
    	private final int calories;//卡路里 可选
    	private final int fat;//脂肪 可选
    	private final int sodium;//纳 可选
    	private final int carbonhydrate;//碳水化合物可选
    	
    	private NutritionFactsBuilder(Builder builder){
    		servingSize = builder.servingSize;
    		servings = builder.servings;
    		calories = builder.calories;
    		fat = builder.fat;
    		sodium = builder.sodium;
    		carbonhydrate = builder.carbonhydrate;
    	}
    	
    	@Override
    	public String toString(){
    		return "NutritionFacts [servingSize="+servingSize+", servings="+servings+",calories="+calories+",fat="+fat+",sodium="+sodium+",carbonhydrate="+carbonhydrate;
    	}
    	public static class Builder{
    		private final int servingSize;//分量大小 必须
    		private final int servings;//使用次数 必须
    		private  int calories = 0;//卡路里 可选
    		private  int fat = 0;//脂肪 可选
    		private  int sodium = 0;//纳 可选
    		private  int carbonhydrate = 0;//碳水化合物可选
    		//2.构造函数中传入必填的成员
    		public Builder(int servingSize,int servings){
    			super();
    			this.servingSize = servingSize; 
    			this.servings = servings;
    		}
    		public Builder setCalories(int calories){
    			this.calories = calories;
    			return this;
    		}
    		public Builder setFat(int fat){
    			this.fat = fat;
    			return this;
    		}
    		public Builder setSodium(int sodium){
    			this.sodium = sodium;
    			return this;
    		}
    		//相当于创造零件的buildPart1方法
    		public Builder setCarbonhydrate(int carbonhydrate){
    			this.carbonhydrate = carbonhydrate;
    			return this;
    		}
    		//Builder中创建真正的对象的方法,相当于retrieveResult方法
    		public NutritionFactsBuilder build(){
    			return new NutritionFactsBuilder(this);
    		}
    	}
    }
    
  6. Client

    //客户端相当于充当了Director的角色,持有一个Builder对象,先通过set方法制造零件,最后调用build方法创建真正的产品
    //同样的构建过程,传入不同的值时,创建出了含有不同属性值的对象
    NutritionFactsBuilder cocaColaBuilder = new NutritionFactsBuilder.Builder(2500, 250).setCalories(1000).setFat(0)
      .setSodium(300).setCarbonhydrate(265).build();
    System.out.println(cocaColaBuilder);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值