模式動機
無論是在現實世界中還是在軟體系統中,都存在一些複雜的對象,它們擁有多個組成部分,如汽車,它包括車輪、方向盤、發動機等各種組件。而對於大多數使用者而言,無須知道這些組件的裝配細節,也幾乎不會使用單獨某個組件,而是使用一輛完整的汽車,可以通過建造者模式對其進行設計與描述,建造者模式可以將組件和其組裝過程分開,一步一步建立一個複雜的對象。使用者只需要指定複雜物件的類型就可以得到該對象,而無須知道其內部的具體構造細節。
在軟體開發中,也存在大量類似汽車一樣的複雜物件,它們擁有一系列成員屬性,這些成員屬性中有些是參考型別的成員對象。而且在這些複雜物件中,還可能存在一些限制條件,如某些屬性沒有賦值則複雜物件不能作為一個完整的產品使用;有些屬性的賦值必須按照某個順序,一個屬性沒有賦值之前,另一個屬性可能無法賦值等。
複雜物件相當於一輛有待建造的汽車,而對象的屬性相當於汽車的組件,建造產品的過程就相當於組合組件的過程。由於組合組件的過程很複雜,因此,這些組件的組合過程往往被“外部化”到一個稱作建造者的對象裡,建造者返還給用戶端的是一個已經建造完畢的完整產品對象,而使用者無須關心該對象所包含的屬性以及它們的組裝方式,這就是建造者模式的模式動機。 模式定義
造者模式(Builder Pattern):將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
建造者模式是一步一步建立一個複雜的對象,它允許使用者只通過指定複雜物件的類型和內容就可以構建它們,使用者不需要知道內部的具體構建細節。建造者模式屬於對象建立型模式。根據中文翻譯的不同,建造者模式又可以稱為產生器模式。 樣本
在Effective Java第二版中,Josh Bloch在第二章中就提到使用Builder模式處理需要很多參數的建構函式。他不僅展示了Builder的使用,也描述了相這種方法相對使用帶很多參數的建構函式帶來的好處。需要指出的是Josh Bloch已經在他的書本貫穿了這一思想。 標準Builder
Person.java
package com.ricky.builder.ch1;/** * ${DESCRIPTION} * * @author Ricky Fung * @create 2016-07-06 16:39 */public class Person { private final String firstName; private final String lastName; private final String middleName; private final int age; private final String street; private final String district; private final String city; private final String province; private final boolean isFemale; private final boolean isEmployed; public Person(String firstName, String lastName, String middleName, int age, String street, String district, String city, String province, boolean isFemale, boolean isEmployed) { this.firstName = firstName; this.lastName = lastName; this.middleName = middleName; this.age = age; this.street = street; this.district = district; this.city = city; this.province = province; this.isFemale = isFemale; this.isEmployed = isEmployed; } @Override public String toString() { return "Person{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", middleName='" + middleName + '\'' + ", age=" + age + ", street='" + street + '\'' + ", district='" + district + '\'' + ", city='" + city + '\'' + ", province='" + province + '\'' + ", isFemale=" + isFemale + ", isEmployed=" + isEmployed + '}'; }}
PersonBuilder
package com.ricky.builder.ch1;/** * ${DESCRIPTION} * * @author Ricky Fung * @create 2016-07-06 16:40 */public class PersonBuilder { private String firstName; private String lastName; private String middleName; private int age; private String street; private String district; private String city; private String province; private boolean isFemale; private boolean isEmployed; public PersonBuilder(){ } public PersonBuilder firstName(String firstName) { this.firstName = firstName; return this; } public PersonBuilder lastName(String lastName) { this.lastName = lastName; return this; } public PersonBuilder middleName(String middleName) { this.middleName = middleName; return this; } public PersonBuilder age(int age) { this.age = age; return this; } public PersonBuilder street(String street) { this.street = street; return this; } public PersonBuilder district(String district) { this.district = district; return this; } public PersonBuilder city(String city) { this.city = city; return this; } public PersonBuilder province(String province) { this.province = province; return this; } public PersonBuilder isFemale(boolean isFemale) { this.isFemale = isFemale; return this; } public PersonBuilder isEmployed(boolean isEmployed) { this.isEmployed = isEmployed; return this; } public Person create() { return new Person(firstName, lastName, middleName, age, street, district, city, province, isFemale, isEmployed); }}
package com.ricky.builder;import com.ricky.builder.ch1.Person;import com.ricky.builder.ch1.PersonBuilder;/** * ${DESCRIPTION} * * @author Ricky Fung * @create 2016-07-06 16:41 */public class PersonBuilderDemo { public static void main(String[] args){ Person person = new PersonBuilder() .firstName("ricky") .lastName("fung") .age(27) .street("建國路") .district("朝陽") .city("北京市") .province("北京") .isFemale(false) .isEmployed(true) .create(); System.out.println(person); }}靜態內部類
SIAMessage.java
package com.ricky.builder.ch2;/** * ${DESCRIPTION} * * @author Ricky Fung * @create 2016-07-06 13:19 */public class SIAMessage { private final String businessCode; private final String type; private final String message; private final int timeout; SIAMessage(String businessCode, String type, String message, int timeout) { this.businessCode = businessCode; this.type = type; this.message = message; this.timeout = timeout; } public static Builder custom(){ return new Builder(); } public String getBusinessCode() { return businessCode; } public String getType() { return type; } public String getMessage() { return message; } public int getTimeout() { return timeout; } public static class Builder { private String businessCode; private String type; private String message; private int timeout; public Builder setBusinessCode(String businessCode) { this.businessCode = businessCode; return this; } public Builder setType(String type) { this.type = type; return this; } public Builder setMessage(String message) { this.message = message; return this; } public Builder setTimeout(int timeout) { this.timeout = timeout; return this; } public SIAMessage build() { initDefaultValue(this); return new SIAMessage(businessCode, message, type, timeout); } private void initDefaultValue(Builder builder) { if(builder.businessCode==null || builder.businessCode.length()<1){ throw new IllegalArgumentException("businessCode can not be empty!"); } if(builder.message==null || builder.message.length()<1){ throw new IllegalArgumentException("message can not be empty!"); } if(builder.type==null || builder.type.length()<1){ //設定預設mime-type builder.type = "application/json"; } if(builder.timeout<1){ builder.timeout = 6*1000; } } } @Override public String toString() { return "SIAMessage{" + "businessCode='" + businessCode + '\'' + ", type='" + type + '\'' + ", message='" + message + '\'' + ", timeout=" + timeout + '}'; }}
用戶端調用
package com.ricky.builder;import com.ricky.builder.ch2.SIAMessage;/** * ${DESCRIPTION} * * @author Ricky Fung * @create 2016-07-06 13:22 */public class SIAMessageDemo { public static void main(String[] args){ //1. SIAMessage message = new SIAMessage.Builder() .setBusinessCode("yrd") .setMessage("hello world") .setTimeout(1000) .build(); System.out.println(message); //2. message = SIAMessage.custom() .setBusinessCode("yrd") .setMessage("hello world") .setTimeout(1000) .build(); System.out.println(message); }}複雜的Builder
Builder嵌套
package com.ricky.builder.ch3;/** * ${DESCRIPTION} * * @author Ricky Fung * @create 2016-07-06 15:38 */public class Car { private final String manufacturer; //製造商 private final String brand; //品牌 private final String model; //型號 private final String producingArea; //產地 private final String producingDate; //生產時間 private final Engine engine; private final Wheel wheel; private final Light light; Car(String manufacturer, String brand, String model, String producingArea, String producingDate, Engine engine, Wheel wheel, Light light) { this.manufacturer = manufacturer; this.brand = brand; this.model = model; this.producingArea = producingArea; this.producingDate = producingDate; this.engine = engine; this.wheel = wheel; this.light = light; } public static class Builder{ private String manufacturer; //製造商 private String brand; //品牌 private String model; //型號 private String producingArea; //產地 private String producingDate; //生產時間 private Engine engine; private Wheel wheel; private Light light; public Builder(String manufacturer, String brand, String model) { this.manufacturer = manufacturer; this.brand = brand; this.model = model; } public Builder producingArea(String producingArea){ this.producingArea = producingArea; return this; } public Builder producingDate(String producingDate){ this.producingDate = producingDate; return this; } public Builder engine(Engine engine){ this.engine = engine; return this; } public Builder wheel(Wheel wheel){ this.wheel = wheel; return this; } public Builder light(Light light){ this.light = light; return this; } public Car build(){ return new Car(manufacturer, brand, model, producingArea, producingDate, engine, wheel, light); } }}
package com.ricky.builder.ch3;/** * 汽車發動機 * * @author Ricky Fung * @create 2016-07-06 15:42 */public class Engine { private final String pl; //排量 private final String maxOutputPower; //最大輸出功率 private final int rpm; //轉速 Engine(String pl, String maxOutputPower, int rpm) { this.pl = pl; this.maxOutputPower = maxOutputPower; this.rpm = rpm; } public static Builder custom(){ return new Builder(); } public static class Builder{ private String pl; //排量 private String maxOutputPower; //最大輸出功率 private int rpm; //轉速 public Builder pl(String pl){ this.pl = pl; return this; } public Builder maxOutputPower(String maxOutputPower){ this.maxOutputPower = maxOutputPower; return this; } public Builder rpm(int rpm){ this.rpm = rpm; return this; } public Engine build(){ return new Engine(pl, maxOutputPower, rpm); } }}
package com.ricky.builder.ch3;/** * 汽車輪子 * * @author Ricky Fung * @create 2016-07-06 15:40 */public class Wheel { private String brand; private String producingDate; Wheel(String brand, String producingDate) { this.brand = brand; this.producingDate = producingDate; } public static Builder custom(){ return new Builder(); } public static class Builder{ private String brand; private String producingDate; public Builder brand(String brand){ this.brand = brand; return this; } public Builder producingDate(String producingDate){ this.producingDate = producingDate; return this; } public Wheel build(){ return new Wheel(brand, producingDate); } }}
package com.ricky.builder.ch3;/** * 車燈 * * @author Ricky Fung * @create 2016-07-06 15:43 */public class Light { private String brand; private String structure; //結構 private String producingDate; Light(String brand, String structure, String producingDate) { this.brand = brand; this.structure = structure; this.producingDate = producingDate; } public static Builder custom(){ return new Builder(); } public static class Builder{ private String brand; private String structure; //結構 private String producingDate; public Builder brand(String brand){ this.brand = brand; return this; } public Builder structure(String structure){ this.structure = structure; return this; } public Builder producingDate(String producingDate){ this.producingDate = producingDate; return thi