2011年5月17日 星期二

Design Pattern - Builder Pattern

Builder pattern, 可以將一個物件內部的"表象"跟物件產生的過程分開來,在建造的過程中產生不同的物件,
簡單的說,如果要把建立複雜組件與運用組件方式分離,就可以使用Builder Pattern.

Builder pattern 的Class diagram :
From wiki:
Builder
Abstract interface for creating objects (product).
Concrete Builder
Provides implementation for Builder. It is an object able to construct other objects. Constructs and assembles parts to build the objects.
Director
The Director class is responsible for managing the correct sequence of object creation. It receives a Concrete Builder as a parameter and executes the necessary operations on it.
Product
The final object that will be created by the Director using Builder.


wiki的例子是用Pizza , Pizza 就是一個Product , 會有一個抽像的PizzaBuilder 用來建立不同的pizza部份, 然後實作HawaiianPizzaBuilder and SpicyPizzaBuilder (ConcreteBuilder),再透過Director - Cook去把完整的pizza 實作出來。

如果我有一個Product , 每個Product 裡面的零件都不一樣, 也能套上Builder Pattern,首先建立一個Product 類別如下:
/** "Product" */
package builder;

import java.util.ArrayList;
import java.util.List;

public class Product {
 List<String> parts = new ArrayList<String>();
 
 public void addPart(String part){
  parts.add(part);
 }
 
 public void showProd(){
  for (int i = 0; i < parts.size(); i  ) {
   System.out.println("Prod infor =  "  parts.get(i));
  }
 }
 
}


再來建立一個Abstract Builder , 用來建立每個Product 的零件
/** "Abstract Builder" */
package builder;

public abstract class PartBuilder {
 protected Product prod;
 
 public Product getProduct(){
  return this.prod;
 }
 
 public void createNewProd(){
  this.prod = new Product();
 }
 
 public abstract void buildeAddPart();
}


然後再建立一個Concrete Builder
/** "ConcreteBuilder" */
package builder;

public class ProdABuilder extends PartBuilder {

 @Override
 public void buildeAddPart() {
  prod.addPart("PART1");  
 }

}

然後是Director的部份,負責把產品建立出來
/** "Director" */
package builder;

public class Director {
 private PartBuilder partbuilder;
 public void setPartbuilder(PartBuilder p){
  this.partbuilder = p;
 }
 
 public Product getProd(){
  return partbuilder.getProduct();
 }
 
 public void createProduct(){
  partbuilder.createNewProd();
  partbuilder.buildeAddPart();
 }
 
}

最後就能使用
package builder;

public class builderpattern {

 /**
  * @param args
  */
 public static void main(String[] args) {
  Director director = new Director();
  PartBuilder prodaBuilder = new ProdABuilder();
    
  director.setPartbuilder(prodaBuilder);
  director.createProduct();
    
  Product prod = director.getProd();
  prod.showProd();

 }

}




在Java Bean 裡面, 一般的Bean 長成這樣,
package builder;

public class UserBeanBuilder {
 private long userId;
 private String userName;
 private String userDesc;
 
 public long getUserId() {
  return userId;
 }
 
 public void setUserId(long userId) {
  this.userId = userId;
 }
 public String getUserName() {
  return userName;
 }
 public void setUserName(String userName) {
  this.userName = userName;
 }
 public String getUserDesc() {
  return userDesc;
 }
 public void setUserDesc(String userDesc) {
  this.userDesc = userDesc;
 }
 
 
 
 

}


但是如果有很多屬性,而我們又必需讓bean建立時有一些初始值, Constructor 就會寫很多個, Builder Pattern 在Effective Java也有提到
Item2 Consider using a Builder when faced with many constructor parameters


經過修改之後Bean 會長這樣
package builder;

public class UserBeanBuilder {
 private long userId;
 private String userName;
 private String userDesc;
 
 public long getUserId() {
  return userId;
 }

 public String getUserName() {
  return userName;
 }

 public String getUserDesc() {
  return userDesc;
 }

 public static class Builder{
  private long userId;
  private String userName;
  private String userDesc;
  
  public Builder(long userId,String userName){
   this.userId = userId;
   this.userName = userName;
  }
  
  public Builder userDesc(String desc) {
   this.userDesc = desc;
   return this;
  }
  
  public UserBeanBuilder build(){
   return new UserBeanBuilder(this);
  }
 }
 
 private UserBeanBuilder(Builder builder){
  this.userId = builder.userId;
  this.userName = builder.userName;
  this.userDesc = builder.userDesc;
 }
}


調用
package builder;

public class UserCreator {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  UserBeanBuilder user = new UserBeanBuilder.Builder(100, "MOMO").userDesc("CHECK THIS").build();
  
  System.out.println(user.getUserId());
  System.out.println(user.getUserName());
  System.out.println(user.getUserDesc());
  
 }

}



利用在一個 class 中內置的 public static class Builder ,並將原先的 constructor 封裝在該 class 中,我們可以免除以下問題:
1:Ugly constructor. 例如:new NutritionFacts(int servingSize, int servings,int calories, int fat, int 15 more optional params!);
2:Constructor telescoping. 如果是上例的 constructor ,我們在使用的時候一定得參看原先的Cnstructor 說明,才能知道各個參數的意義。如:NutritionFacts locoCola =new NutritionFacts(240, 8, 0, 0, 30, 28); ,無法了解這串數字是什麼
3:Too many setters required when creating objects.


Reference :
Wikipedia - Builder pattern

Book Review: Effective Java 2nd Edition

沒有留言:

張貼留言