zoukankan      html  css  js  c++  java
  • 【HeadFirst 设计模式学习笔记】16 建筑者(Builder)模式拾零

    作者:gnuhpc
    出处:http://www.cnblogs.com/gnuhpc/

    1.概述

    这个模式在Head First中没有介绍,大概是因为这个模式和工厂模式有点像。其目的主要是通过创建简单的对象构建一个复杂的对象。

    2.举例

    举一个例子:创建汽车的相关指标说明。

    首先我们创建一个汽车的类:

      1: public class Car {
    
      2:  private boolean powerSteering = false;
    
      3:  private boolean powerWindow = false;
    
      4:  private String price = "";
    
      5:  public boolean isPowerSteargin() {
    
      6:  return powerSteering;
    
      7:  }
    
      8:  public void setPowerSteargin(boolean aPowerSteering) {
    
      9:  this.powerSteering = aPowerSteering;
    
     10:  }
    
     11:  public boolean isPowerWindow() {
    
     12:  return powerWindow;
    
     13:  }
    
     14:  public void setPowerWindow(boolean aPowerWindow) {
    
     15:  this.powerWindow = aPowerWindow;
    
     16:  }
    
     17:  public String getPrice() {
    
     18:  return price;
    
     19:  }
    
     20:  public void setPrice(String aPrice) {
    
     21:  this.price = aPrice;
    
     22:  }
    
     23:  //print the output
    
     24:  public String toString(){
    
     25:  String output = "";
    
     26:  output += "Power Steering : "+powerSteering+"/n";
    
     27:  output += "Power Window : "+powerWindow+"/n";
    
     28:  output += "Price : Rs "+price+"/n";
    
     29:  return output;
    
     30:  }
    
     31: }
    
     32: 

    在上边的这个例子中,关于一个汽车的指标说明有三项:Power Steering、Power Window和Price。

    然后我们创建一个创建汽车的类CarBuilder :

      1: abstract class CarBuilder {
    
      2:  protected Car car;
    
      3:  
    
      4:  public Car getCar() {
    
      5:  return car;
    
      6:  }
    
      7:  
    
      8:  public void createNewCar() {
    
      9:  car = new Car();
    
     10:  }
    
     11:  public abstract void setPowerSteering();
    
     12:  public abstract void setPowerWindow();
    
     13:  public abstract void setPrice();
    
     14: }
    
     15: 

    现在我们创建两个具体汽车类型的创建者:Alto_LX 和 Alto_LXI

      1: public class Alto_LX extends CarBuilder{
    
      2:  
    
      3:  @Override
    
      4:  public void setPowerSteering() {
    
      5:  // TODO Auto-generated method stub
    
      6:  car.setPowerSteargin(false);
    
      7:  }
    
      8:  
    
      9:  @Override
    
     10:  public void setPowerWindow() {
    
     11:  // TODO Auto-generated method stub
    
     12:  car.setPowerWindow(false);
    
     13:  }
    
     14:  
    
     15:  @Override
    
     16:  public void setPrice() {
    
     17:  // TODO Auto-generated method stub
    
     18:  car.setPrice("225000");
    
     19:  }
    
     20:  
    
     21: }
    
     22: 
      1: public class Alto_LXI extends CarBuilder{
    
      2:  
    
      3:  @Override
    
      4:  public void setPowerSteering() {
    
      5:  // TODO Auto-generated method stub
    
      6:  car.setPowerSteargin(true);
    
      7:  }
    
      8:  
    
      9:  @Override
    
     10:  public void setPowerWindow() {
    
     11:  // TODO Auto-generated method stub
    
     12:  car.setPowerWindow(true);
    
     13:  }
    
     14:  
    
     15:  @Override
    
     16:  public void setPrice() {
    
     17:  // TODO Auto-generated method stub
    
     18:  car.setPrice("285000");
    
     19:  }
    
     20: }
    
     21: 

    最后我们再写一个创建说明并生产汽车的类,这个类组合了具体生产汽车的类,所以该类的一个作用就是生产一部汽车(因为组合而得到的能力),另一个作用是为这个汽车创建相关指标说明(这也是因为组合得到的能力,同时也是进行了封装后提供的接口):

      1: public class Specifications {
    
      2:  private CarBuilder carBuilder;
    
      3:  public void setCarBuilder(CarBuilder aCarBuilder){
    
      4:  carBuilder = aCarBuilder;
    
      5:  }
    
      6:  public Car getCar(){
    
      7:  return carBuilder.getCar();
    
      8:  }
    
      9:  
    
     10:  public void constructSpecifications(){
    
     11:  carBuilder.createNewCar();
    
     12:  carBuilder.setPowerSteering();
    
     13:  carBuilder.setPowerWindow();
    
     14:  carBuilder.setPrice();
    
     15:  }
    
     16: }
    
     17: 

    现在我们就可以造两辆带有指标说明的车了:

      1: public class AltoCarBuilderExample {
    
      2:  public static void main(String[] args) {
    
      3:  Specifications spec = new Specifications();
    
      4:  CarBuilder alto_lx = new Alto_LX();
    
      5:  CarBuilder alto_lxi = new Alto_LXI();
    
      6:  
    
      7:  //getting spec for alto lx
    
      8:  spec.setCarBuilder(alto_lx);
    
      9:  spec.constructSpecifications();
    
     10:  Car alto_lx_car = spec.getCar();
    
     11:  System.out.println("Specifiction for Alto LX");
    
     12:  System.out.println(alto_lx_car.toString());
    
     13:  
    
     14:  //getting spec for alto lxi
    
     15:  spec.setCarBuilder(alto_lxi);
    
     16:  spec.constructSpecifications();
    
     17:  Car alto_lxi_car = spec.getCar();
    
     18:  System.out.println("Specifiction for Alto LXI");
    
     19:  System.out.println(alto_lxi_car.toString());
    
     20:  }
    
     21:  
    
     22: }
    
     23: 

    我们可以看到,首先我们实例化了一个创建说明的类,并且实例化了两个生产汽车的类。随后利用创建汽车能力的创建说明类来完成说明和汽车的构造。

    抽象一下,UML图为:

    Builder_pattern

    本例中的Specification就是该图中Director。

    3.与工厂方法(或抽象工厂方法)比较

    该方法强调利用封装一步步完成一个复杂对象的构建,比如本例中的Specifications就是通过创建汽车、创建三项说明来完成汽车的最终构建的。而抽象工厂方法强调构建一系列产品对象,不管其复杂与否。另外,工厂方法还会根据传入的参数进行对象构建,这个建筑者模式中并没有。

    Effective Java, 2nd Edition中提到:The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters.

    我们经常见过这样设计的类:

      1: Pizza(int size) { ... }        
    
      2: Pizza(int size, boolean cheese) { ... }    
    
      3: Pizza(int size, boolean cheese, boolean pepperoni) { ... }    
    
      4: Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }
    
      5: 

    你可以做如下改进:

      1: Pizza pizza = new Pizza(12);
    
      2: pizza.setCheese(true);
    
      3: pizza.setPepperoni(true);
    
      4: pizza.setBacon(true);
    

    这样做也有问题:通过若干步创建的pizza可能会导致对象创建的不完整,并且对于线程安全还需要多多考量。

    此时我们可以使用构建者模式(此处使用嵌套类,其实和组合是一个效果):

      1: public class Pizza {
    
      2:   private int size;
    
      3:   private boolean cheese;
    
      4:   private boolean pepperoni;
    
      5:   private boolean bacon; 
    
      6: 
    
      7:   public static class Builder {
    
      8:     //required
    
      9:     private final int size; 
    
     10: 
    
     11:     //optional
    
     12:     private boolean cheese = false;
    
     13:     private boolean pepperoni = false;
    
     14:     private boolean bacon = false; 
    
     15: 
    
     16:     public Builder(int size) {
    
     17:       this.size = size;
    
     18:     } 
    
     19: 
    
     20:     public Builder cheese(boolean value) {
    
     21:       cheese = value;
    
     22:       return this;
    
     23:     } 
    
     24: 
    
     25:     public Builder pepperoni(boolean value) {
    
     26:       pepperoni = value;
    
     27:       return this;
    
     28:     } 
    
     29: 
    
     30:     public Builder bacon(boolean value) {
    
     31:       bacon = value;
    
     32:       return this;
    
     33:     } 
    
     34: 
    
     35:     public Pizza build() {
    
     36:       return new Pizza(this);
    
     37:     }
    
     38:   } 
    
     39: 
    
     40:   private Pizza(Builder builder) {
    
     41:     size = builder.size;
    
     42:     cheese = builder.cheese;
    
     43:     pepperoni = builder.pepperoni;
    
     44:     bacon = builder.bacon;
    
     45:   }
    
     46: }
    
     47: 

    现在我们创建一个Pizza就是如下的方式:

      1: Pizza pizza = new Pizza.Builder(12).cheese(true).pepperoni(true).bacon(true).build();

    这个例子可以与工厂模式里面的例子对比一下,就知道这两个模式的区别了。

    Java中的DocumentBuilderFactory , StringBuffer, StringBuilder 都是这个模式的例子。

    在线视频:http://v.youku.com/v_show/id_XMjU2OTQ5Mzg4.html

    作者:gnuhpc
    出处:http://www.cnblogs.com/gnuhpc/

  • 相关阅读:
    python入坑级
    nginx配置文件详解
    nginx看端口使用情况
    linux安装nginx
    linux安装jdk1.7
    linux设置tomcat开机启动
    redis master配置了密码进行主从同步
    linux搭建mysql 5.6.28
    linux搭建redis数据库
    找出一组数里出现频率最高的3个数(1.3)
  • 原文地址:https://www.cnblogs.com/gnuhpc/p/2827640.html
Copyright © 2011-2022 走看看