设计模式8—Builder设计模式

Builder建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们,用户不需要了解所构建对象的内部具体构建细节,Builder建造设计模式的目的是为了将构造复杂对象的过程和它的部件解耦。

Builder建造者设计模式有两个重要角色:Director指导者和Builder建造者。Director指导者相当于设计师或架构师,拥有整个产品各个部件之间关系的构建蓝图。Builder建造者是部件的具体创建者,Builder建造者根据Director指导者的指示创建产品的各个部件,最终由Director构建出完整产品。Builder建造者设计模式的UML图如下:

《Effective java》中有一个关于建造者设计模式的例子如下:

用一个类表示包装食品外面显示的营业成分,其中每份含量和每罐含量是必须的,卡路里、脂肪、钠和碳水化合物是可选参数。

大家一般习惯使用重载构造函数来解决该问题,代理如下:

public class NutritionFacts{

  //必须参数

  private final int servingSize;

  private final int servings;

 //可选参数

  private final int calories;

  private final int fat;

  private final int sodium;

  private final int carbohydrate;

  public NutritionFacts(int servingSize,int servings){

     this(servingSize,servings,0);

  }

 public NutritionFacts(int servingSize,int servings,int calories){

    this(servingSize,servings,calories,0);

 }

 public NutritionFacts(int servingSize,int servings,int calories,int fat) {

     this(servingSize,servings,calories,fat,0);

 }

 public NutritionFacts(int servingSize,int servings,int calories,int fat,int sodium) {

     this(servingSize,servings,calories,fat,sodium,0);

}

 public NutritionFacts(int servingSize,int servings,int calories,int fat,int sodium,int carbohydrate){

    this.servingSize = servingSize;

    this.servings = servings;

    this.calories = calories;

    this.fat = fat;

    this.sodium = sodium;

    this.carbohydrate = carbohydrate;

  }

}

如果想要创建实例对象的时候,就利用参数列表最短的构造方法:

NutritionFacts cocaCola = new NutritionFacts(240,8,100,0,35,27);

如果可选参数很多的时候就难以控制,客户端代码变得很难编写,且难以阅读,如果不小心参数顺序混淆了,在编译的时候很难发现问题,在运行时出错难以调试。

public class NutritionFacts{

  //必须参数

  private int servingSize = -1;

  private int servings = -1;

 //可选参数

   private int calories = 0;

   private int fat = 0;

   private int sodium = 0;

   private int carbohydrate = 0;

   public NutritionFacts(){}

   public void setServingSize(int val){

        this.servingSize = val;

   }

   public void setServings(int val){

      this.servings = val;

    }

   public void setCalories(int val){

       this.calories = val;

   }

   public void setSodium(int val){

      this.sodium = val;

   }

   public void setCarbohydrate(int val){

       this.carbohydrate = val;

   }

  使用setter方法可以弥补重载构造方法的缺陷,创建实例对象很容易,并且代码也容易阅读:

由于javaBean自身有着很严重的缺点,构造过程被分到了几个调用中,在构造过程中java Bean可能处于不一致的状态,类无法仅仅通过校验构造方法参数的有效性来保证一致性,使用处于不一致状态的对象将会导致失败。另外javaBean阻止了把类做成不可变的可能,因此很难确保线程安全。

解决构造参数多问题的最佳方案是使用建造者模式,代码如下:

public class NutritionFacts {

  //必须参数

   private final int servingSize;

   private final int servings;

 //可选参数

   private final int calories;

   private final int fat;

   private final int sodium;

   private final int carbohydrate;

  //建造者

  public static class Builder {

       //必须参数

         private final int servingSize;

         private final int servings;

      //可选参数

         private final int calories = 0;

         private final int fat = 0;

         private final int sodium = 0;

         private final int carbohydrate = 0;

         public Builder(int servingSize,int servings){

             this.servingSize = servingSize;

             this.servings = servings;

         }

        public  Builder calories(int val){

                this.calories = val;

               return this;

         }

         public Builder fat(int val){

               this.fat = val;

               return this;

         }

        public Builder sodium(int val){

              this.sodium = val;

              return this;

        }

       public Builder carbohydrate(int val){

             this.carbohydrate = val;

              return this;

       }

       public NutritionFacts build(){

              return new NutritionFacts(this);

       }

}

  private NutritionFacts(Builder builder){

        this.servingSize = builder.servingSize;

        this.servings = builder.servings;

        this.calories = builder.calories;

        this.fat = builder.fat;

        this.sodium = builder.sodium;

        this.carbohydrate = builder.carbohydrate;

   }

}

使用构造者模式创建实例对象:

NutritionFacts cocaCola = new NutritionFacts().Build(240,8),calories(100).sodium(35).carbohydrate(27),build();

建造者模式可以有多个可变参数,可以利用建造器构建多个对象,参数可以在创建时动态调整。

Builder建造者模式和AbstractFactory抽象工厂模式的区别:

Builder建造者模式和AbstractFactory抽象工厂模式非常类似,很多人经常分不清楚,区别如下:

(1)抽象工厂模式中,每一次工厂物件被呼叫时都会传回一个完整的产品物件,而使用端有可能会决定把这些产品组装成一个更大的和复杂的产品,也有可能不会。工厂物件是没有状态的,不知道上一次构建的是哪一个产品,也没有未来的概念,不知道下一次构建的是哪一个产品,更不知道自己构建的产品在更高层的产品结构蓝图中是什么位置。

(2)建造者模式不同,建造者模式的重点在指导者(Director)角色。指导者是有状态的,它知道整体蓝图,知道上一次、这一次和下一次交给建造者(Builder)角色去构建的零件是什么,以便能够将这些零件组装成一个更大规模的产品。它一点一点地构造出一个复杂的产品,而这个产品的组装程序就是发生在指导者角色内部,建造者模式的使用端拿到的是一个完整的最后产品。

换言之,虽然抽象工厂模式与建造者模式都是设计模式,但是抽象工厂模式处在更加具体的尺度上,而建造模式则处在更加宏观的尺度上。一个系统可以由一个建造模式和一个抽象工厂模式组成,使用端通过呼叫这个导演角色,间接地呼叫另一个抽象工厂模式的工厂角色。工厂样式传回不同产品族的零件,而建造者模式则把它们组装起来。

JDK中建造者模式的应用:

StringBuilder和StringBuffer的append()方法使用了建造者模式。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值