04 設計模式之生成器模式
我是陳皮,一個在互聯網 Coding 的 ITer,微信搜索「陳皮的JavaLib」第一時間閱讀最新文章,回復【資料】,即可獲得我精心整理的技術資料,電子書籍,一線大廠面試資料和優(yōu)秀簡歷模板。

1 背景
我們知道,構建一輛汽車是極其復雜,部件繁多的。假設我們現在需要構建一輛汽車,為方便演示,我們假設構建一輛汽車只需要發(fā)動機,車輪,方向盤。那如何去構建一輛汽車呢?假設構建一輛車的順序為安裝發(fā)動機,安裝車輪,安裝方向盤。
首先,需要定義三個部件(發(fā)動機,車輪,方向盤),將它們定義為單獨的類。
package com.chenpi.builder; /** * @Description 發(fā)動機 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class Engine { // 品牌 private String brand; public Engine(String brand) { this.brand = brand; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } }
package com.chenpi.builder; /** * @Description 車輪 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class Wheel { // 車輪類型 private String type; public Wheel(String type) { this.type = type; } public String getType() { return type; } public void setType(String type) { this.type = type; } }
package com.chenpi.builder; /** * @Description 方向盤 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class SteeringWheel { // 方向盤顏色 private String color; public SteeringWheel(String color) { this.color = color; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
然后定義汽車類,并且由以上三種部件組成。
package com.chenpi.builder; /** * @Description 汽車 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class Car { // 發(fā)動機 private Engine engine; // 方向盤 private SteeringWheel steeringWheel; // 車輪 private Wheel wheel; public Car(Engine engine, SteeringWheel steeringWheel, Wheel wheel) { this.engine = engine; this.steeringWheel = steeringWheel; this.wheel = wheel; } // 省略 getter / setter 方法 }
最后客戶端負責生產各個汽車組件,并組裝構建一輛汽車。
package com.chenpi.builder; /** * @Description 客戶端 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class ChenPiClient { public static void main(String[] args) { Engine engine = new Engine("沃爾沃"); Wheel wheel = new Wheel("175/70R"); SteeringWheel steeringWheel = new SteeringWheel("Black"); Car car = new Car(engine, steeringWheel, wheel); System.out.println(car); } } // 輸出結果如下 Car{engine=Engine{brand='沃爾沃'}, steeringWheel=SteeringWheel{color='Red'}, wheel=Wheel{type='175/70R'}}
此時,如果我們需要創(chuàng)建另外一種類型的汽車,那我們就需要重寫客戶端代碼,再進行生產,組裝構建一輛新汽車。
Engine engine = new Engine("蔚來"); Wheel wheel = new Wheel("255/55 R19"); SteeringWheel steeringWheel = new SteeringWheel("Black"); Car car = new Car(engine, steeringWheel, wheel);
你會發(fā)現客戶端不僅要清楚整輛汽車的外觀表示,還需要清楚汽車組件的裝配細節(jié),然后按順序組裝構建汽車對象,這對客戶端的使用是不友好的,使用比較麻煩。而且這演示的只是簡單的幾個屬性的賦值,如果對象屬性比較多,構建過程復雜就更不用說了。
對于客戶端來說,它不應該知道汽車內部在裝配細節(jié),它就只想要一輛完整的汽車來使用。
2 生成器模式(Builder Pattern)
生成器模式(也叫建造者模式)是一種創(chuàng)建型設計模式,它將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示。
即有些對象的創(chuàng)建流程是一樣的,但是創(chuàng)建出來的對象一些自身特性可以是不一樣的,所以需要將對象的表示(外觀)和對象具體的構建分開來。
生成器模式把對象的構建步驟抽象成生成器(builder),指導類(director)對所有生成步驟的先后順序進行控制。客戶端使用指導類并傳入相應的生成器,通過指導類的接口便可以得到相應的對象。
Builder:生成器接口,定義了創(chuàng)建一個 Product 對象所需的各個部件的操作,以及獲取產品的方法。
ConcreteBuilder:生成器接口具體實現類,實現各個部件的創(chuàng)建并負責組裝,并返回構建好的產品對象。
Director:指導者,使用 Builder 接口,以一個統一的過程來構建所需要的 Product 對象。
Product:產品,被生成器構建的復雜對象,包含多個部件。
現在我們使用生成器模式改寫開頭的例子,我們首先需要定義一個生成器接口。
package com.chenpi.builder; /** * @Description 汽車生成器接口 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public interface CarBuilder { Car builder(); void buildEngine(); void buildWheel(); void buildSteeringWheel(); }
假設我們現在需要沃爾沃的汽車,那么就定義一個沃爾沃的生成器接口實現類。
package com.chenpi.builder; /** * @Description 沃爾沃汽車生成器 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class VolvoCarBuilder implements CarBuilder { private Car car = new Car(); @Override public Car builder() { return car; } @Override public void buildEngine() { car.setEngine(new Engine("沃爾沃")); } @Override public void buildWheel() { car.setWheel(new Wheel("175/70R")); } @Override public void buildSteeringWheel() { car.setSteeringWheel(new SteeringWheel("Black")); } }
再定義汽車指導者,負責指導汽車產品的構建。
package com.chenpi.builder; /** * @Description 汽車指導者 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class CarDirector { private CarBuilder builder; public CarDirector(CarBuilder builder) { this.builder = builder; } public void buildCar() { builder.buildEngine(); builder.buildWheel(); builder.buildSteeringWheel(); } public Car getCar() { return builder.builder(); } }
最后,客戶端就選擇特定的生成器實現類對象,傳遞給指導類對象,讓它生產我們需要的產品對象即可。
package com.chenpi.builder; /** * @Description 客戶端 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class ChenPiClient { public static void main(String[] args) { CarDirector director = new CarDirector(new VolvoCarBuilder()); director.buildCar(); Car car = director.getCar(); System.out.println(car); } } // 輸出結果如下 Car{engine=Engine{brand='沃爾沃'}, steeringWheel=SteeringWheel{color='Black'}, wheel=Wheel{type='175/70R'}}
3 生成器模式簡化版
生成器模式的主要功能是構建復雜的產品,而且是細化的、分步驟的構建產品,也就是生成器模式重在一步一步解決構造復雜對象的問題。
你是否發(fā)現上述的生成器模式會比較復雜,定義太多的接口和類等。其實實際使用中會根據情況進行調整。例如上述的生成器對象和被構建的對象是分開的,那其實客戶端可以直接使用 new 被構建對象的方式來創(chuàng)建產品對象,這樣就導致生成器模式荒廢了,所以我們可以將生成器對象合并到被構建的對象中去,稱為它的一個內部類。
而且我們也可以不再需要指導類,直接讓客戶端使用生成器對象進行構建最終產品。
假設我們現在需要一個 Mongo 客戶端參數選項對象,來進行 Mongo 客戶端的配置,那我們就可以定義一個客戶端參選選項類,存放各種參數,并且將生成器定義為它的內部類,用來構建 Mongo 客戶端參數選項對象。
實際開發(fā)中,Mongo 客戶端配置參數是很多的,可達十幾二十多個,為方便演示,我們只定義幾個。這些參數值通過生成器來進行配置構建,并對參數合理值進行校驗。
而且我們將 MongoClientOptions 類的構造方法定義為私有的,所以外部只能通過生成器來構建 MongoClientOptions 對象。
package com.chenpi.builder.mongo; /** * @Description mongo客戶端參數選項 * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class MongoClientOptions { private final int maxWaitTime; private final int maxConnectionIdleTime; private final int maxConnectionLifeTime; private final int connectTimeout; private final int socketTimeout; private MongoClientOptions(final Builder builder) { maxWaitTime = builder.maxWaitTime; maxConnectionIdleTime = builder.maxConnectionIdleTime; maxConnectionLifeTime = builder.maxConnectionLifeTime; connectTimeout = builder.connectTimeout; socketTimeout = builder.socketTimeout; } // 創(chuàng)建一個生成器對象 public static Builder builder() { return new Builder(); } public static class Builder { private int maxWaitTime = 1000 * 60 * 2; private int maxConnectionIdleTime = 0; private int maxConnectionLifeTime = 0; private int connectTimeout = 1000 * 10; private int socketTimeout = 0; public Builder maxWaitTime(final int maxWaitTime) { this.maxWaitTime = maxWaitTime; return this; } public Builder maxConnectionIdleTime(final int maxConnectionIdleTime) { this.maxConnectionIdleTime = maxConnectionIdleTime; return this; } public Builder maxConnectionLifeTime(final int maxConnectionLifeTime) { this.maxConnectionLifeTime = maxConnectionLifeTime; return this; } public Builder connectTimeout(final int connectTimeout) { if (connectTimeout < 0) { throw new RuntimeException("connectTimeout must be >= 0"); } this.connectTimeout = connectTimeout; return this; } public Builder socketTimeout(final int socketTimeout) { this.socketTimeout = socketTimeout; return this; } // 構建一個最終的mongo客戶端選項對象 public MongoClientOptions build() { return new MongoClientOptions(this); } } }
接下來,我們演示如何使用生成器來構建一個 MongoClientOptions 對象。使用鏈式編程的方式,很簡單輕松的就構建出我們需要的對象了。
package com.chenpi.builder.mongo; /** * @Description * @Author 陳皮 * @Date 2021/8/8 * @Version 1.0 */ public class ChenPiClient { public static void main(String[] args) { MongoClientOptions mongoClientOptions = MongoClientOptions.builder().connectTimeout(1000) .maxConnectionIdleTime(60000) .maxConnectionLifeTime(600000).maxWaitTime(1000).socketTimeout(5000).build(); } }
本次分享到此結束啦~~
如果覺得文章對你有幫助,、、關注、評論,您的支持就是我創(chuàng)作最大的動力!
Java
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。