將一個複雜對象的構建與它的表象分離,使得一樣的構建能夠建立不一樣的表示。bash
相同的方法,不一樣的執行順序,產生不一樣的事件結果時app
多個部件或零件,均可以裝配到一個對象中,可是產生的運行結果又不相同時ide
產品類很是複雜,或者產品類中的調用順序產生不一樣做用時ui
初始化一個對象特別複雜時this
經典的Builder模式通常擁有4個角色spa
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供客人選擇。code
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()供給客人點餐,而食物的品種咱們並不知道,具體的食物和套餐是交給子類去實現的。對象
public class SubMealBuilderA extends MealBuilder {
@Override
public void buildFood() {
meal.setFood("一個雞腿堡");
}
@Override
public void buildDrink() {
meal.setDrink("一杯可樂");
}
}
複製代碼
SubMealBuilderA 是做爲ConcreteBuilder的,他給出了一個具體實現方案---套餐A( 包含一個雞腿堡和一杯可樂)事件
public class SubMealBuilderB extends MealBuilder {
@Override
public void buildFood() {
meal.setFood("一個雞肉卷");
}
@Override
public void buildDrink() {
meal.setDrink("一杯果汁");
}
}
複製代碼
SubMealBuilderB 也是做爲ConcreteBuilder的,他給出了一個具體實現方案---套餐B( 包含一個雞肉卷和一杯果汁)rem
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個類進行修改了。