前言
在面向對象的軟件開發中,構建復雜對象時經常會遇到許多挑戰。一種常見的解決方案是使用設計模式,其中建造者模式是一個強大而靈活的選擇。本文將深入探討建造者模式的原理、結構、優點以及如何在實際項目中應用它。
一、復雜的對象
public class Computer {private String ram;private String hardDisk;private String cpu;// ...可能還有更多參數...public Computer(String ram, String hardDisk, String cpu) {this.ram = ram;this.hardDisk = hardDisk;this.cpu = cpu;// ...初始化更多參數...}// getters and setters...
}// 使用構造函數創建對象,參數多且容易混淆
Computer computer = new Computer("16GB", "1TB", "Intel i7");
上面代碼比較簡單,但是如果 Computer 類有更多的參數,構造函數將會變得非常長,而且在創建 Computer 對象時,傳遞參數的順序非常重要,一旦順序錯誤,就會創建出一個配置錯誤的對象。此外,如果參數有默認值,那么用戶還需要記住哪些參數是必須的,哪些是可選的,這增加了使用的復雜性。
我們可以總結出創建復雜對象時可能會遇到的問題包括:
- 參數過多:構造函數或者 setter 方法的參數可能會非常多,導致代碼難以閱讀和維護。
- 參數順序:容易混淆參數的順序,特別是當有多個相同類型的參數時。
- 不夠靈活:如果創建過程中需要多個步驟,使用構造函數或 setter 方法就不夠靈活。
- 不可讀性:代碼的可讀性差,特別是在沒有注釋的情況下,很難理解每個參數的意義。
二、建造者模式
建造者模式是一種創建型
設計模式,旨在將復雜對象的構建過程與其表示分離。通過使用建造者模式,可以使客戶端代碼與對象的內部結構解耦,從而使構建過程更加靈活,并且更易于維護和擴展。
三、建造者模式的核心組成部分
建造者模式通常包括以下幾個關鍵組件:
- 產品(Product):表示被構建的復雜對象。產品類通常包含多個屬性,并且可能包含一些復雜的業務邏輯。
- 抽象建造者(Builder):定義了構建產品所需的接口。抽象建造者通常包括一系列方法來構建產品的各個部分。
- 具體建造者(Concrete Builder):實現了抽象建造者接口,負責實際構建產品的各個部分,并提供方法來獲取最終的產品實例。
- 指揮者(Director):負責調用建造者的方法來構建產品,但不直接創建產品的實例。指揮者通常根據一定的構建步驟來組織構建過程。
四、運用建造者模式
場景假設:我們要構建一臺計算機,它有不同的部件,比如 CPU、內存、硬盤等。我們將使用建造者模式來構建這臺計算機。
-
定義產品類: 首先,確定需要構建的復雜對象的屬性和方法,并創建相應的產品類。這個產品類應該包含對象的所有屬性,并提供相應的 getter 和 setter 方法。
/*** 產品類:計算機*/ public class Computer {private String cpu; // CPUprivate String memory; // 內存private String hardDisk; // 硬盤// 省略構造函數和 getter/setter方法 }
-
創建抽象建造者接口: 定義一個抽象建造者接口,該接口包含構建產品各個部件的抽象方法。這些方法代表構建產品所需的不同步驟。
/*** 抽象建造者接口*/ public interface ComputerBuilder {// 構建CPUvoid buildCPU();// 構建內存void buildMemory();// 構建硬盤void buildHardDisk();// 構建計算機Computer build(); }
-
實現具體建造者類: 創建一個或多個具體建造者類,實現抽象建造者接口。每個具體建造者類負責實現構建產品各個部件的具體方法,并在最后返回構建好的產品。
/*** 具體建造者類:桌面計算機建造者*/ public class DesktopComputerBuilder implements ComputerBuilder {private Computer computer; // 待構建的計算機對象public DesktopComputerBuilder() {this.computer = new Computer();}@Overridepublic void buildCPU() {computer.setCpu("Intel Core i7");}@Overridepublic void buildMemory() {computer.setMemory("16GB DDR4");}@Overridepublic void buildHardDisk() {computer.setHardDisk("1TB SSD");}@Overridepublic Computer build() {// 返回構建好的計算機對象return computer;} }
-
創建指揮者類: 定義一個指揮者類,該類負責使用具體建造者對象構建最終的產品。指揮者類知道構建者的具體實現細節,但與產品的實際構建過程無關。
/*** 指揮者類:計算機指揮者*/ public class ComputerDirector {private ComputerBuilder computerBuilder; // 建造者對象public ComputerDirector(ComputerBuilder computerBuilder) {this.computerBuilder = computerBuilder;}// 使用建造者構建計算機對象public Computer construct() {// 按照順序調用建造者的方法來構建計算機computerBuilder.buildCPU();computerBuilder.buildMemory();computerBuilder.buildHardDisk();// 返回構建好的計算機對象return computerBuilder.build();} }
-
使用建造者模式構建對象: 在客戶端代碼中,創建具體的建造者對象,并將其傳遞給指揮者類。然后,通過指揮者類調用相應的方法來構建產品。最終,客戶端代碼可以獲取構建好的產品并使用它。
public class Main {public static void main(String[] args) {// 創建桌面計算機的建造者對象ComputerBuilder desktopBuilder = new DesktopComputerBuilder();// 創建計算機指揮者對象,并傳入桌面計算機的建造者ComputerDirector director = new ComputerDirector(desktopBuilder);// 使用指揮者構建計算機對象Computer desktop = director.construct();// 輸出構建好的桌面計算機的配置信息System.out.println("Desktop Computer Configuration:");System.out.println(desktop);} }/*在一些簡單的情況下,可以省略指揮者(Director)類。特別是當只有一個具體建造者(Concrete Builder)時,客戶端代碼可以直接調用具體建造者的方法來構建產品,而不需要指揮者類。 */ public class Main {public static void main(String[] args) {// 創建桌面計算機的建造者對象ComputerBuilder desktopBuilder = new DesktopComputerBuilder();// 使用桌面計算機的建造者直接構建計算機對象Computer desktop = desktopBuilder.buildCPU().buildMemory().buildHardDisk().build();// 輸出構建好的桌面計算機的配置信息System.out.println("Desktop Computer Configuration:");System.out.println(desktop);} }
在上面的例子中,我們定義了 Computer 類作為產品,ComputerBuilder 作為具體建造者來創建產品
,ComputerDirector 作為指導者確定創建產品時遵循的步驟流程
。客戶端代碼只需通過 ComputerDirector 便能構建 Computer 對象,這樣就隱藏了構建細節,使得客戶端代碼更加簡潔和易于維護。
五、建造者模式的應用場景
建造者模式適用于以下幾種場景:
-
復雜對象的創建:當創建的對象非常復雜,包含多個組成部分時,建造者模式可以幫助管理復雜性,使得代碼更加清晰。
// 假設我們需要創建一個復雜的 Pizza 對象,它包含多種配料和選項。 // 產品類 public class Pizza {private String dough;private String sauce;private String topping;// 私有構造器private Pizza(Builder builder) {this.dough = builder.dough;this.sauce = builder.sauce;this.topping = builder.topping;}// Builder類public static class Builder {private String dough;private String sauce;private String topping;public Builder withDough(String dough) {this.dough = dough;return this;}public Builder withSauce(String sauce) {this.sauce = sauce;return this;}public Builder withTopping(String topping) {this.topping = topping;return this;}public Pizza build() {return new Pizza(this);}} }// 客戶端代碼 // 在創建 Pizza 時,可以根據需要選擇是否設置 dough、sauce、topping Pizza pizza = new Pizza.Builder().withDough("cross").withSauce("mild").withTopping("ham and pineapple").build();
-
構造過程需要分步驟進行:如果一個對象的構造過程需要分多個步驟或者階段來完成,建造者模式允許你逐步構造對象,而不是一次性通過一個巨大的構造函數完成。
// 倘若 House 對象的構建,需要分步驟設置地基、結構、屋頂和內部裝修。 // 產品類 public class House {private String foundation;private String structure;private String roof;private String interior;private House(Builder builder) {this.foundation = builder.foundation;this.structure = builder.structure;this.roof = builder.roof;this.interior = builder.interior;}// Builder類public static class Builder {private String foundation;private String structure;private String roof;private String interior;public Builder withFoundation(String foundation) {this.foundation = foundation;return this;}public Builder withStructure(String structure) {this.structure = structure;return this;}public Builder withRoof(String roof) {this.roof = roof;return this;}public Builder withInterior(String interior) {this.interior = interior;return this;}public House build() {return new House(this);}} }// 客戶端代碼 // 在創建對象之前,可嚴格管控步驟的先后順序 House house = new House.Builder().withFoundation("concrete").withStructure("wooden").withRoof("shingle").withInterior("painted").build();
-
同一構建過程不同表示:當需要根據不同的需求和過程來創建不同的對象表示時,建造者模式提供了很好的解決方案。
// 如果我們有一個 Car 對象,它可以有不同的配置,例如經濟型和豪華型。 // 產品類 public class Car {private String engine;private String seats;private String navigationSystem;private Car(Builder builder) {this.engine = builder.engine;this.seats = builder.seats;this.navigationSystem = builder.navigationSystem;}// Builder類public static class Builder {private String engine;private String seats;private String navigationSystem;public Builder withEngine(String engine) {this.engine = engine;return this;}public Builder withSeats(String seats) {this.seats = seats;return this;}public Builder withNavigationSystem(String navigationSystem) {this.navigationSystem = navigationSystem;return this;}public Car build() {return new Car(this);}} }// 客戶端代碼 // 根據不同配置,滿足不同需求 Car economyCar = new Car.Builder().withEngine("1.5L").withSeats("cloth").build();Car luxuryCar = new Car.Builder().withEngine("3.0L V6").withSeats("leather").withNavigationSystem("advanced").build();
-
參數多且復雜:當構造函數的參數非常多,且有些可能是可選的,建造者模式可以幫助組織這些參數,使得構造函數不會過于龐大和復雜。
// 倘若 Order 對象可能包含多個可選的配置項,如禮品包裝、快遞服務、優惠券等, // 使用建造者模式可以讓客戶端代碼清晰地指定所需的配置。 // 產品類:具有多個配置參數,其中一些是可選的。 public class Order {private String product;private boolean giftWrap;private boolean expressDelivery;private String couponCode;// 私有構造器,只能通過Builder類構建Order對象private Order(Builder builder) {this.product = builder.product;this.giftWrap = builder.giftWrap;this.expressDelivery = builder.expressDelivery;this.couponCode = builder.couponCode;}// Builder 類:提供了一個鏈式 API 來設置 Order 對象的屬性public static class Builder {private String product;private boolean giftWrap;private boolean expressDelivery;private String couponCode;public Builder(String product) {this.product = product;}public Builder setGiftWrap(boolean giftWrap) {this.giftWrap = giftWrap;return this;}public Builder setExpressDelivery(boolean expressDelivery) {this.expressDelivery = expressDelivery;return this;}public Builder setCouponCode(String couponCode) {this.couponCode = couponCode;return this;}// 構建方法:創建一個Order對象并返回public Order build() {return new Order(this);}} }// 客戶端使用:創建一個 Order 對象 Order order = new Order.Builder("Product1").setGiftWrap(true).setExpressDelivery(true).setCouponCode("DISCOUNT10").build();
六、小結
建造者模式是一種創建復雜對象的有效方式,它允許我們按照步驟構建對象,從而使得構建過程更加靈活和可配置。例如,在 Java 中,StringBuilder
是建造者模式的一個典型應用,它允許我們通過多個方法調用來構建最終的字符串,而不是一次性傳入所有的字符串內容。另一個例子是流式 API,如 Java 8 的 Stream
API,它允許通過鏈式調用來構建復雜的查詢。通過將構建過程與表示分離,建造者模式提高了代碼的可讀性和可維護性,同時也使得代碼更加易于擴展和重用。
推薦閱讀
- Spring 三級緩存
- 深入了解 MyBatis 插件:定制化你的持久層框架
- Zookeeper 注冊中心:單機部署
- 【JavaScript】探索 JavaScript 中的解構賦值
- 深入理解 JavaScript 中的 Promise、async 和 await