生成器设计模式示例
本文是我們名為“ Java設(shè)計模式 ”的學(xué)院課程的一部分。
在本課程中,您將深入研究大量的設(shè)計模式,并了解如何在Java中實現(xiàn)和利用它們。 您將了解模式如此重要的原因,并了解何時以及如何應(yīng)用模式中的每一個。 在這里查看 !
目錄
1.建造者模式 2.什么是構(gòu)建器模式 3.實施構(gòu)建器模式 4. Builder模式的另一種形式 5.何時使用構(gòu)建器模式 6. JDK中的構(gòu)建器模式 7.下載源代碼建造者模式
通常,對象構(gòu)造的細節(jié)(例如,實例化和初始化組成對象的組件)通常作為對象構(gòu)造函數(shù)的一部分保留在對象內(nèi)。 這種設(shè)計將對象的構(gòu)建過程與組成對象的組件緊密地聯(lián)系在一起。 只要正在構(gòu)造的對象簡單且對象的構(gòu)造過程是確定的,并且始終產(chǎn)生相同的對象表示,則此方法適用。
但是,當(dāng)要創(chuàng)建的對象很復(fù)雜并且構(gòu)成對象創(chuàng)建過程的一系列步驟可以以不同的方式執(zhí)行,從而產(chǎn)生對象的不同表示形式時,此設(shè)計可能無效。 因為構(gòu)造過程的不同實現(xiàn)都保存在對象中,所以對象可能變得笨重(構(gòu)造膨脹)并且模塊化程度降低。 隨后,添加新的實現(xiàn)或?qū)ΜF(xiàn)有實現(xiàn)進行更改需要對現(xiàn)有代碼進行更改。
使用Builder模式,可以更有效地設(shè)計構(gòu)造此類對象的過程。 Builder模式建議將構(gòu)造邏輯從對象類中移到一個單獨的類中,稱為Builder類。 可以有多個這樣的構(gòu)建器類,每種構(gòu)建器類對于構(gòu)建對象的一系列步驟都具有不同的實現(xiàn)。 每個構(gòu)建器實現(xiàn)都會導(dǎo)致對象的不同表示形式。
為了說明構(gòu)建器模式的使用,讓我們幫助一家汽車公司使用圖形模型向其客戶顯示其不同的汽車。 該公司有一個圖形工具,可以在屏幕上顯示汽車。 該工具的要求是??為其提供汽車對象。 汽車對象應(yīng)包含汽車的規(guī)格。 圖形工具使用這些規(guī)格來顯示汽車。 該公司已將其汽車分為轎車或跑車等不同類別。 只有一個汽車對象,我們的工作是根據(jù)分類創(chuàng)建汽車對象。 例如,對于轎車,應(yīng)構(gòu)建符合轎車規(guī)格的汽車對象,或者,如果需要跑車,則應(yīng)構(gòu)建符合跑車規(guī)格的汽車對象。 目前,公司僅需要這兩種類型的汽車,但將來可能還會需要其他類型的汽車。
我們將創(chuàng)建兩個不同的構(gòu)建器,每種分類中的一個,即轎車和跑車。 這兩個建造者將幫助我們根據(jù)其規(guī)格建造汽車物體。 但是在此之前,讓我們討論“構(gòu)建器模式”的一些細節(jié)。
2.什么是構(gòu)建器模式
構(gòu)建器模式的目的是將復(fù)雜對象的構(gòu)造與其表示分離,以便同一構(gòu)造過程可以創(chuàng)建不同的表示。 這種類型的分離減小了物體的尺寸。 事實證明,該設(shè)計具有更高的模塊化,每個實現(xiàn)都包含在不同的構(gòu)建器對象中。 添加新的實現(xiàn)(即添加新的構(gòu)建器)變得更加容易。 對象構(gòu)造過程變得獨立于組成對象的組件。 這提供了對對象構(gòu)造過程的更多控制。
在實現(xiàn)方面,可以將構(gòu)建過程中的每個不同步驟聲明為要由不同的混凝土構(gòu)建者實現(xiàn)的公共接口的方法。
客戶端對象可以創(chuàng)建具體構(gòu)建器的實例,并調(diào)用構(gòu)造最終對象的不同部分所需的方法集。 這種方法要求每個客戶對象都了解構(gòu)造邏輯。 每當(dāng)構(gòu)造邏輯發(fā)生變化時,所有客戶端對象都需要進行相應(yīng)的修改。
構(gòu)建器模式引入了另一種隔離級別,可以解決此問題。 構(gòu)建器模式建議不要使用客戶端對象直接調(diào)用不同的構(gòu)建器方法,而建議使用稱為Director的專用對象,該對象負(fù)責(zé)調(diào)用構(gòu)建最終對象所需的不同構(gòu)建器方法。 不同的客戶端對象可以使用Director對象創(chuàng)建所需的對象。 構(gòu)造對象后,客戶端對象可以直接向構(gòu)建器請求完全構(gòu)造的對象。 為了簡化此過程,可以在公共Builder界面中聲明一個新方法getObject ,以由不同的具體構(gòu)建器實現(xiàn)。
新的設(shè)計消除了使用客戶端對象處理構(gòu)成對象構(gòu)造過程的方法的需要,并封裝了如何從客戶端構(gòu)造對象的細節(jié)。
圖1
建造者
- 指定用于創(chuàng)建產(chǎn)品對象各部分的抽象接口。
混凝土建筑工
- 通過實現(xiàn)Builder接口來構(gòu)造和組裝產(chǎn)品的各個部分。
- 定義并跟蹤其創(chuàng)建的表示形式。
- 提供用于檢索產(chǎn)品的界面。
導(dǎo)向器
- 使用Builder接口構(gòu)造一個對象。
產(chǎn)品
- 表示正在構(gòu)造的復(fù)雜對象。 ConcreteBuilder構(gòu)建產(chǎn)品的內(nèi)部表示形式,并定義其組裝過程。
- 包括定義組成零件的類,包括用于將零件組裝成最終結(jié)果的接口。
3.實施構(gòu)建器模式
打擊是“ Car類(產(chǎn)品),其中包含一些用于構(gòu)造完整car對象所需的汽車重要組件。
package com.javacodegeeks.patterns.builderpattern;public class Car {private String bodyStyle;private String power;private String engine;private String breaks;private String seats;private String windows;private String fuelType;private String carType;public Car (String carType){this.carType = carType;}public String getBodyStyle() {return bodyStyle;}public void setBodyStyle(String bodyStyle) {this.bodyStyle = bodyStyle;}public String getPower() {return power;}public void setPower(String power) {this.power = power;}public String getEngine() {return engine;}public void setEngine(String engine) {this.engine = engine;}public String getBreaks() {return breaks;}public void setBreaks(String breaks) {this.breaks = breaks;}public String getSeats() {return seats;}public void setSeats(String seats) {this.seats = seats;}public String getWindows() {return windows;}public void setWindows(String windows) {this.windows = windows;}public String getFuelType() {return fuelType;}public void setFuelType(String fuelType) {this.fuelType = fuelType;}@Overridepublic String toString(){StringBuilder sb = new StringBuilder();sb.append("--------------"+carType+"--------------------- \\n");sb.append(" Body: ");sb.append(bodyStyle);sb.append("\\n Power: ");sb.append(power);sb.append("\\n Engine: ");sb.append(engine);sb.append("\\n Breaks: ");sb.append(breaks);sb.append("\\n Seats: ");sb.append(seats);sb.append("\\n Windows: ");sb.append(windows);sb.append("\\n Fuel Type: ");sb.append(fuelType);return sb.toString();} }CarBuilder是構(gòu)建器接口,包含用于構(gòu)建car對象及其組件的一組常用方法。
package com.javacodegeeks.patterns.builderpattern;public interface CarBuilder {public void buildBodyStyle();public void buildPower();public void buildEngine();public void buildBreaks();public void buildSeats();public void buildWindows();public void buildFuelType();public Car getCar(); }getCar方法用于在構(gòu)造完成后將最終的汽車對象返回給客戶端。
讓我們看一下CarBuilder接口的兩種實現(xiàn),一種適用于每種汽車,即轎車和跑車。
package com.javacodegeeks.patterns.builderpattern;public class SedanCarBuilder implements CarBuilder{private final Car car = new Car("SEDAN");@Overridepublic void buildBodyStyle() {car.setBodyStyle("External dimensions: overall length (inches): 202.9, " +"overall width (inches): 76.2, overall height (inches): 60.7, wheelbase (inches): 112.9," +" front track (inches): 65.3, rear track (inches): 65.5 and curb to curb turning circle (feet): 39.5");}@Overridepublic void buildPower(){car.setPower("285 hp @ 6,500 rpm; 253 ft lb of torque @ 4,000 rpm");}@Overridepublic void buildEngine() {car.setEngine("3.5L Duramax V 6 DOHC");}@Overridepublic void buildBreaks() {car.setBreaks("Four-wheel disc brakes: two ventilated. Electronic brake distribution");}@Overridepublic void buildSeats() {car.setSeats("Front seat center armrest.Rear seat center armrest.Split-folding rear seats");}@Overridepublic void buildWindows() {car.setWindows("Laminated side windows.Fixed rear window with defroster");}@Overridepublic void buildFuelType() {car.setFuelType("Gasoline 19 MPG city, 29 MPG highway, 23 MPG combined and 437 mi. range");}@Overridepublic Car getCar(){return car;}}package com.javacodegeeks.patterns.builderpattern;public class SportsCarBuilder implements CarBuilder{private final Car car = new Car("SPORTS");@Overridepublic void buildBodyStyle() {car.setBodyStyle("External dimensions: overall length (inches): 192.3," +" overall width (inches): 75.5, overall height (inches): 54.2, wheelbase (inches): 112.3," +" front track (inches): 63.7, rear track (inches): 64.1 and curb to curb turning circle (feet): 37.7");}@Overridepublic void buildPower(){car.setPower("323 hp @ 6,800 rpm; 278 ft lb of torque @ 4,800 rpm");}@Overridepublic void buildEngine() {car.setEngine("3.6L V 6 DOHC and variable valve timing");}@Overridepublic void buildBreaks() {car.setBreaks("Four-wheel disc brakes: two ventilated. Electronic brake distribution. StabiliTrak stability control");}@Overridepublic void buildSeats() {car.setSeats("Driver sports front seat with one power adjustments manual height, front passenger seat sports front seat with one power adjustments");}@Overridepublic void buildWindows() {car.setWindows("Front windows with one-touch on two windows");}@Overridepublic void buildFuelType() {car.setFuelType("Gasoline 17 MPG city, 28 MPG highway, 20 MPG combined and 380 mi. range");}@Overridepublic Car getCar(){return car;}}上面的兩個建造者根據(jù)所需規(guī)范創(chuàng)建和構(gòu)造產(chǎn)品,即car對象。
現(xiàn)在,讓我們測試Builder。
package com.javacodegeeks.patterns.builderpattern;public class TestBuilderPattern {public static void main(String[] args) {CarBuilder carBuilder = new SedanCarBuilder();CarDirector director = new CarDirector(carBuilder);director.build();Car car = carBuilder.getCar();System.out.println(car);carBuilder = new SportsCarBuilder();director = new CarDirector(carBuilder);director.build();car = carBuilder.getCar();System.out.println(car);}}上面的代碼將導(dǎo)致以下輸出。
--------------SEDAN--------------------- Body: External dimensions: overall length (inches): 202.9, overall width (inches): 76.2, overall height (inches): 60.7, wheelbase (inches): 112.9, front track (inches): 65.3, rear track (inches): 65.5 and curb to curb turning circle (feet): 39.5Power: 285 hp @ 6,500 rpm; 253 ft lb of torque @ 4,000 rpmEngine: 3.5L Duramax V 6 DOHCBreaks: Four-wheel disc brakes: two ventilated. Electronic brake distributionSeats: Front seat center armrest.Rear seat center armrest.Split-folding rear seatsWindows: Laminated side windows.Fixed rear window with defrosterFuel Type: Gasoline 19 MPG city, 29 MPG highway, 23 MPG combined and 437 mi. range --------------SPORTS--------------------- Body: External dimensions: overall length (inches): 192.3, overall width (inches): 75.5, overall height (inches): 54.2, wheelbase (inches): 112.3, front track (inches): 63.7, rear track (inches): 64.1 and curb to curb turning circle (feet): 37.7Power: 323 hp @ 6,800 rpm; 278 ft lb of torque @ 4,800 rpmEngine: 3.6L V 6 DOHC and variable valve timingBreaks: Four-wheel disc brakes: two ventilated. Electronic brake distribution. StabiliTrak stability controlSeats: Driver sports front seat with one power adjustments manual height, front passenger seat sports front seat with one power adjustmentsWindows: Front windows with one-touch on two windowsFuel Type: Gasoline 17 MPG city, 28 MPG highway, 20 MPG combined and 380 mi. range在上面的類,我們首先創(chuàng)建一個SedanCarBuilder和CarDirector 。 然后,我們要求CarDirector根據(jù)傳遞給它的builder為我們制造car 。 最后,我們直接要求builder向我們提供創(chuàng)建的car對象。
我們對SportsCarBuilder進行了同樣的操作,該car根據(jù)跑車規(guī)范返回car對象。
使用“構(gòu)建器模式”的方法足夠靈活,可以在將來更改任何新型汽車而無需更改任何現(xiàn)有代碼。 我們所需要做的就是根據(jù)新車的規(guī)格創(chuàng)建一個新的建造者,并將其提供給總監(jiān)進行建造。
4. Builder模式的另一種形式
到目前為止,我們還沒有看到其他形式的構(gòu)建器模式。 有時,一個對象的屬性列表很長,并且其中大多數(shù)屬性都是可選的。 考慮一個在線表格,需要填寫該表格才能成為站點的成員。 您需要填寫所有必填字段,但是可以跳過可選字段,有時填充某些可選字段可能看起來很有價值。
請檢查下面的Form類,其中包含一長串屬性,并且某些屬性是可選的。 表單中必須具有firstName , lastName , userName和password ,但其他所有字段均為可選字段。
package com.javacodegeeks.patterns.builderpattern;import java.util.Date;public class Form {private String firstName;private String lastName;private String userName;private String password;private String address;private Date dob;private String email;private String backupEmail;private String spouseName;private String city;private String state;private String country;private String language;private String passwordHint;private String secuirtyQuestion;private String securityAnswer;}問題是,我們應(yīng)該為此類編寫什么樣的構(gòu)造函數(shù)? 編寫具有長參數(shù)列表的構(gòu)造函數(shù)不是一個好選擇,這可能會使客戶端感到沮喪,尤其是在重要字段很少的情況下。 這可能會增加錯誤的范圍; 客戶可能會誤將值提供給錯誤的字段。
另一種方法是使用伸縮構(gòu)造函數(shù),其中為構(gòu)造函數(shù)僅提供必需的參數(shù),為另一個構(gòu)造函數(shù)提供一個可選參數(shù),為第三個構(gòu)造函數(shù)提供兩個可選參數(shù),依此類推,最終構(gòu)造出具有所有可選參數(shù)的構(gòu)造函數(shù)。
伸縮構(gòu)造函數(shù)可能看起來像這樣。
public Form(String firstName,String lastName){this(firstName,lastName,null,null); }public Form(String firstName,String lastName,String userName,String password){this(firstName,lastName,userName,password,null,null); }public Form(String firstName,String lastName,String userName,String password,String address,Date dob){this(firstName,lastName,userName,password,address,dob,null,null); }public Form(String firstName,String lastName,String userName,String password,String address,Date dob,String email,String backupEmail){… }當(dāng)您要創(chuàng)建實例時,請使用帶有最短參數(shù)列表的構(gòu)造函數(shù),該列表包含要設(shè)置的所有參數(shù)。
伸縮式構(gòu)造函數(shù)可以工作,但是當(dāng)有許多參數(shù)時,很難編寫客戶端代碼,甚至更難讀取。 讀者不知道所有這些值意味著什么,必須仔細計數(shù)參數(shù)才能找出答案。 相同類型的參數(shù)的長序列可能會導(dǎo)致細微的錯誤。 如果客戶端意外地反轉(zhuǎn)了兩個這樣的參數(shù),則編譯器不會抱怨,但是程序在運行時會出現(xiàn)異常。
當(dāng)面對許多構(gòu)造函數(shù)參數(shù)時,第二種替代方法是JavaBeans模式,在該模式中,您調(diào)用一個參數(shù)較少的構(gòu)造函數(shù)來創(chuàng)建對象,然后調(diào)用setter方法來設(shè)置每個所需的參數(shù)和每個感興趣的可選參數(shù)。
不幸的是,JavaBeans模式本身具有嚴(yán)重的缺點。 因為構(gòu)造是在多個調(diào)用之間進行的,所以JavaBean在構(gòu)造過程中可能處于不一致的狀態(tài)。 該類不能僅通過檢查構(gòu)造函數(shù)參數(shù)的有效性來強制執(zhí)行一致性。 在對象處于不一致狀態(tài)時嘗試使用該對象可能會導(dǎo)致失敗,而這些失敗與包含該錯誤的代碼相距甚遠,因此難以調(diào)試。
第三種選擇是將伸縮構(gòu)造函數(shù)模式的安全性與JavaBeans模式的可讀性結(jié)合在一起。 它是Builder模式的一種形式。 客戶端不是直接制作所需的對象,而是使用所有必需的參數(shù)調(diào)用構(gòu)造函數(shù)并獲取一個生成器對象。 然后,客戶端在構(gòu)建器對象上調(diào)用類似于setter的方法來設(shè)置每個感興趣的可選參數(shù)。 最后,客戶端調(diào)用無參數(shù)構(gòu)建方法來生成對象。
package com.javacodegeeks.patterns.builderpattern;import java.util.Date;public class Form {private String firstName;private String lastName;private String userName;private String password;private String address;private Date dob;private String email;private String backupEmail;private String spouseName;private String city;private String state;private String country;private String language;private String passwordHint;private String securityQuestion;private String securityAnswer;public static class FormBuilder {private String firstName;private String lastName;private String userName;private String password;private String address;private Date dob;private String email;private String backupEmail;private String spouseName;private String city;private String state;private String country;private String language;private String passwordHint;private String securityQuestion;private String securityAnswer;public FormBuilder(String firstName, String lastName, String userName, String password){this.firstName = firstName;this.lastName = lastName;this.userName = userName;this.password = password;}public FormBuilder address(String address){this.address = address;return this;}public FormBuilder dob(Date dob){this.dob = dob;return this;}public FormBuilder email(String email){this.email = email;return this;}public FormBuilder backupEmail(String backupEmail){this.backupEmail = backupEmail;return this;}public FormBuilder spouseName(String spouseName){this.spouseName = spouseName;return this;}public FormBuilder city(String city){this.city = city;return this;}public FormBuilder state(String state){this.state = state;return this;}public FormBuilder country(String country){this.country = country;return this;}public FormBuilder language(String language){this.language = language;return this;}public FormBuilder passwordHint(String passwordHint){this.passwordHint = passwordHint;return this;}public FormBuilder securityQuestion(String securityQuestion){this.securityQuestion = securityQuestion;return this;}public FormBuilder securityAnswer(String securityAnswer){this.securityAnswer = securityAnswer;return this;}public Form build(){return new Form(this);}}private Form(FormBuilder formBuilder){this.firstName = formBuilder.firstName;this.lastName = formBuilder.lastName;this.userName = formBuilder.userName;this.password = formBuilder.password;this.address = formBuilder.address;this.dob = formBuilder.dob;this.email = formBuilder.email;this.backupEmail = formBuilder.backupEmail;this.spouseName = formBuilder.spouseName;this.city = formBuilder.city;this.state = formBuilder.state;this.country = formBuilder.country;this.language = formBuilder.language;this.passwordHint = formBuilder.passwordHint;this.securityQuestion = formBuilder.securityQuestion;this.securityAnswer = formBuilder.securityAnswer;}@Overridepublic String toString(){StringBuilder sb = new StringBuilder();sb.append(" First Name: ");sb.append(firstName);sb.append("\\n Last Name: ");sb.append(lastName);sb.append("\\n User Name: ");sb.append(userName);sb.append("\\n Password: ");sb.append(password);if(this.address!=null){sb.append("\\n Address: ");sb.append(address);}if(this.dob!=null){sb.append("\\n DOB: ");sb.append(dob);}if(this.email!=null){sb.append("\\n Email: ");sb.append(email);}if(this.backupEmail!=null){sb.append("\\n Backup Email: ");sb.append(backupEmail);}if(this.spouseName!=null){sb.append("\\n Spouse Name: ");sb.append(spouseName);}if(this.city!=null){sb.append("\\n City: ");sb.append(city);}if(this.state!=null){sb.append("\\n State: ");sb.append(state);}if(this.country!=null){sb.append("\\n Country: ");sb.append(country);}if(this.language!=null){sb.append("\\n Language: ");sb.append(language);}if(this.passwordHint!=null){sb.append("\\n Password Hint: ");sb.append(passwordHint);}if(this.securityQuestion!=null){sb.append("\\n Security Question: ");sb.append(securityQuestion);}if(this.securityAnswer!=null){sb.append("\\n Security Answer: ");sb.append(securityAnswer);}return sb.toString();}public static void main(String[] args) {Form form = new Form.FormBuilder("Dave", "Carter", "DavCarter", "DAvCaEr123").passwordHint("MyName").city("NY").language("English").build();System.out.println(form);}}上面的代碼將產(chǎn)生以下輸出:
First Name: DaveLast Name: CarterUser Name: DavCarterPassword: DAvCaEr123City: NYLanguage: EnglishPassword Hint: MyName您可以清楚地看到,現(xiàn)在客戶只需要提供必填字段和對他來說很重要的字段。 現(xiàn)在要創(chuàng)建表單對象,我們需要調(diào)用FormBuilder構(gòu)造函數(shù),該構(gòu)造函數(shù)接受必填字段,然后我們需要在其上調(diào)用一組必需的方法,最后調(diào)用build方法來獲取表單對象。
5.何時使用構(gòu)建器模式
在以下情況下使用構(gòu)建器模式
- 創(chuàng)建復(fù)雜對象的算法應(yīng)獨立于組成對象的零件及其組裝方式。
- 構(gòu)造過程必須允許所構(gòu)造的對象具有不同的表示形式。
6. JDK中的構(gòu)建器模式
- java.lang.StringBuilder#append() (未同步)
- java.lang.StringBuffer#append() (已同步)
- java.nio.ByteBuffer#put() (同樣在CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer和DoubleBuffer上)
- javax.swing.GroupLayout.Group#addComponent()
- java.lang.Appendable所有實現(xiàn)
7.下載源代碼
這是關(guān)于“構(gòu)建器模式”的一課。 您可以在此處下載源代碼: Builder Pattern Project
翻譯自: https://www.javacodegeeks.com/2015/09/builder-design-pattern.html
總結(jié)
- 上一篇: 安卓的碎片化问题(安卓的碎片化)
- 下一篇: 外墙设计模式示例