將一個複雜對象的構建與它的表象分離,使得一樣的構建能夠建立不一樣的表示。bash
相同的方法,不一樣的執行順序,產生不一樣的事件結果時markdown
多個部件或零件,均可以裝配到一個對象中,可是產生的運行結果又不相同時app
產品類很是複雜,或者產品類中的調用順序產生不一樣做用時ide
初始化一個對象特別複雜時ui
經典的Builder模式通常擁有4個角色this
public class Meal { private String food; private String drink; public String getFood() { return food; } public void setFood(String food) { this.food = food; } public String getDrink() { return drink; } public void setDrink(String drink) { this.drink = drink; } } 複製代碼
菜單類Meal在這裏充當Product的角色,提供了事物food和飲料drink供客人選擇。spa
public abstract class MealBuilder { protected Meal meal = new Meal(); public abstract void buildFood(); public abstract void buildDrink(); public Meal getMeal() { return meal; } } 複製代碼
MealBuilder是一個抽象Builder,提供了2個抽象方法buildFood()和buildDrink()供給客人點餐,而食物的品種咱們並不知道,具體的食物和套餐是交給子類去實現的。code
public class SubMealBuilderA extends MealBuilder { @Override public void buildFood() { meal.setFood("一個雞腿堡"); } @Override public void buildDrink() { meal.setDrink("一杯可樂"); } } 複製代碼
SubMealBuilderA 是做爲ConcreteBuilder的,他給出了一個具體實現方案---套餐A( 包含一個雞腿堡和一杯可樂)orm
public class SubMealBuilderB extends MealBuilder { @Override public void buildFood() { meal.setFood("一個雞肉卷"); } @Override public void buildDrink() { meal.setDrink("一杯果汁"); } } 複製代碼
SubMealBuilderB 也是做爲ConcreteBuilder的,他給出了一個具體實現方案---套餐B( 包含一個雞肉卷和一杯果汁)對象
public class Waiter { private MealBuilder mealBuilder; public void setMealBuilder(MealBuilder mealBuilder) { this.mealBuilder = mealBuilder; } public Meal construct() { mealBuilder.buildDrink(); mealBuilder.buildFood(); return mealBuilder.getMeal(); } } 複製代碼
Waiter類充當Director的角色,客人將選好的套餐告訴服務員後,服務員將客人選擇的食物組裝起來而且通知後廚開始生產。
public class Test { public static void main(String[] args) { Waiter waiter = new Waiter(); waiter.setMealBuilder(new SubMealBuilderA()); Meal meal = waiter.construct(); log(meal); waiter.setMealBuilder(new SubMealBuilderB()); meal = waiter.construct(); log(meal); } private static void log(Meal meal) { System.out.println(meal.getClass() + ":" + meal.getFood() + ":" + meal.getDrink()); } } 複製代碼
運行結果
class com.example.jc.myapplication.builder.custom.Meal:一個雞腿堡:一杯可樂
class com.example.jc.myapplication.builder.custom.Meal:一個雞肉卷:一杯果汁
複製代碼
在實際中咱們更常見的Builder模式是有對經典模式又必定修改的。如AlertDialog的Builer模式,它的AlertDialog.Builer同時扮演了Builder、 ConcreteBuilder、 Director的角色,簡化了Builer的使用。同時這種形式的Builer也更常見。
public class WaiterX { private MenuController mWaiter; WaiterX() { mWaiter = new MenuController(); } public static class Builder { private MenuController.MenuParams p; public Builder() { p = new MenuController.MenuParams(); } public Builder setFood(String food) { p.food = food; return this; } public Builder setDrink(String drink) { p.drink = drink; return this; } public Builder setCount(int count) { p.count = count; return this; } public Builder setType(int type) { p.type = type; return this; } public Builder setRemark(String remark) { p.remark = remark; return this; } public WaiterX create() { WaiterX waiter = new WaiterX(); p.apply(waiter.mWaiter); return waiter; } } @Override public String toString() { return "WaiterX{" + "mWaiter=" + mWaiter + '}'; } } 複製代碼
在WaiterX中經過一個靜態內部類Builder來負責調度產品的組件,再經過create()方法將組裝後的總體返回。而具體的裝細節被隱藏了,經過交由MenuController.MenuParams去專門處理細節信息。
public class MenuController { private String food; private String drink; /** * 點單分量 */ private int count; /** * 類型 外帶,食堂 */ private int type; /** * 備註 */ private String remark; public void setFood(String food) { this.food = food; } public void setDrink(String drink) { this.drink = drink; } public void setCount(int count) { this.count = count; } public void setType(int type) { this.type = type; } public void setRemark(String remark) { this.remark = remark; } public static class MenuParams { public String food; public String drink; public int count; public int type; public String remark; public void apply(MenuController waiter) { if (drink != null) { waiter.setDrink(drink); } if (food != null) { waiter.setFood(food); } if (count > 1) { waiter.setCount(count); } else { waiter.setCount(1); } if (type > 0) { waiter.setType(type); } else { waiter.setType(0); } if (remark != null) { waiter.setRemark(remark); } } } @Override public String toString() { return "MenuController{" + (food != null ? "food='" + food + '\'' : "") + (drink != null ? ", drink='" + drink + '\'' : "") + (count > 0 ? ", count='" + count + '\'' : "") + (type > 0 ? ", type='" + type + '\'' : "") + (remark != null ? ", remark='" + remark + '\'' : "") + '}'; } } 複製代碼
MenuController負責餐點的具體細節處理,用於記錄客戶具體點了什麼食物、分量、需求等信息
public class Test { public static void main(String[] args) { WaiterX waiterX = new WaiterX.Builder().setFood("香辣雞腿堡") .setDrink("可樂") .setType(2) .setRemark("少鹽") .setCount(3) .create(); System.out.println(waiterX.toString()); } } 複製代碼
結果
WaiterX{mWaiter=MenuController{food='香辣雞腿堡', drink='可樂', count='3', type='2', remark='少鹽'}} 複製代碼
經過這種形式,咱們在調用的時候就能採用鏈式調用的方式來處理了,使用起來更加方便。
缺點是 當有新的元素或者新的屬性須要添加時候,須要對多個類進行修改。如 要添加一個字段 取餐時間time,則WaiterX和MenuController中都必須對這個字段time進行支持,這就務必要對2個類進行修改了。