設(shè)計(jì)模式系列之建造者模式
本文轉(zhuǎn)載自微信公眾號「狼王編程」,作者狼王 。轉(zhuǎn)載本文請聯(lián)系狼王編程公眾號。
1、概述
建造者模式是一種創(chuàng)建型設(shè)計(jì)模式, 使你能夠分步驟創(chuàng)建復(fù)雜對象。該模式允許你使用相同的創(chuàng)建代碼生成不同類型和形式的對象。
2、適用場景
1)避免重疊構(gòu)造函數(shù),例如:一個(gè)類有很多的屬性,這時(shí)候的構(gòu)造方法需要傳遞很多的參數(shù),為了避免這樣,會重新寫一個(gè)相對參數(shù)較少的構(gòu)造方法,但是仍然需要對其他參數(shù)進(jìn)行賦默認(rèn)值。
2)當(dāng)需要創(chuàng)建不同的產(chǎn)品類型,此處指比較接近的產(chǎn)品類型,則可以在頂層builder中包含大部分產(chǎn)品屬性的賦值方法。
3、實(shí)例
有以下場景,我們分別使用常規(guī)方式和建造者模式實(shí)現(xiàn):
- 當(dāng)前有一個(gè)汽車工廠,可以生產(chǎn)的汽車類型有ordinary,sport。
- 除了汽車之外,同樣可以生產(chǎn)汽車的操作手冊manual。
- 汽車有以下的屬性:
- 1、type 類型
- 2、seats 座位數(shù)
- 3、engine 引擎
- 4、GPS 導(dǎo)航
- 分別實(shí)現(xiàn)汽車和手冊的生產(chǎn)過程
不使用建造者模式
分別創(chuàng)建車car和手冊manual,以及其內(nèi)部的屬性,當(dāng)前例子中車和手冊是相同的屬性。
- @Data
- public class Car {
- private CarType type;
- private int seats;
- private Engine engine;
- private GPS GPS;
- public Car(CarType type, int seats, Engine engine, com.cloud.bssp.designpatterns.builder.withouttdesign.entity.GPS GPS) {
- this.type = type;
- this.seats = seats;
- this.engine = engine;
- this.GPS = GPS;
- }
- public void detail() {
- System.out.println("this is " + type + " car");
- System.out.println("the seats is :" + seats);
- System.out.println("the engine is :" + engine);
- System.out.println("this GPS is :" + GPS);
- }
- }
- @Data
- public class Manual {
- private CarType type;
- private int seats;
- private Engine engine;
- private GPS GPS;
- public Manual(CarType type, int seats, Engine engine, com.cloud.bssp.designpatterns.builder.withouttdesign.entity.GPS GPS) {
- this.type = type;
- this.seats = seats;
- this.engine = engine;
- this.GPS = GPS;
- }
- public void detail() {
- System.out.println("this is " + type + " car");
- System.out.println("the seats is :" + seats);
- System.out.println("the engine is :" + engine);
- System.out.println("this GPS is :" + GPS);
- }
- }
- public enum CarType {
- SPORT,ORDINARY;
- }
- /**
- * 汽車引擎
- */
- @Data
- public class Engine {
- /**
- * 排量
- */
- private final double volume;
- /**
- * 里程
- */
- private double mileage;
- public Engine(double volume, double mileage) {
- this.volume = volume;
- this.mileage = mileage;
- }
- }
- @Data
- public class GPS {
- /**
- * 路線
- */
- private String route;
- public GPS(String route) {
- this.route = route;
- }
- }
測試類:
- /**
- * 客戶端
- */
- @RunWith(SpringRunner.class)
- @SpringBootTest(classes = TestApplication.class)
- public class TestDemo {
- @Test
- public void test() {
- //生產(chǎn)跑車
- Car sportCar = new Car(
- CarType.SPORT,
- 2,
- new Engine(3.0, 0),
- new GPS("上海東方明珠塔到上海動物園")
- );
- sportCar.detail();
- System.out.println("----------------------------------------");
- //生產(chǎn)普通汽車
- Car ordinaryCar = new Car(
- CarType.ORDINARY,
- 4,
- new Engine(2.0, 0),
- new GPS("上海東方明珠塔到上海動物園")
- );
- ordinaryCar.detail();
- System.out.println("----------------------------------------");
- //生產(chǎn)汽車操作手冊
- Manual manual = new Manual(
- CarType.ORDINARY,
- 4,
- new Engine(2.0, 0),
- new GPS("上海東方明珠塔到上海動物園")
- );
- manual.detail();
- System.out.println("----------------------------------------");
- }
- }
結(jié)果:
- this is SPORT car
- the seats is :2
- the engine is :Engine(volume=3.0, mileage=0.0)
- this GPS is :GPS(route=上海東方明珠塔到上海動物園)
- ----------------------------------------
- this is ORDINARY car
- the seats is :4
- the engine is :Engine(volume=2.0, mileage=0.0)
- this GPS is :GPS(route=上海東方明珠塔到上海動物園)
- ----------------------------------------
- this is ORDINARY car
- the seats is :4
- the engine is :Engine(volume=2.0, mileage=0.0)
- this GPS is :GPS(route=上海東方明珠塔到上海動物園)
- ----------------------------------------
使用建造者模式實(shí)現(xiàn)
使用建造者模式后,代碼要比上面的方法多了不少:
創(chuàng)建頂層Builder
- public interface Builder {
- void setCarType(CarType carType);
- void setSeats(int seats);
- void setEngine(Engine engine);
- void setGPS(GPS gps);
- }
創(chuàng)建實(shí)體類,與上面是相同的,這里不重復(fù)了。
創(chuàng)建car的builder:
- @Data
- public class CarBuilder implements Builder {
- private CarType carType;
- private int seats;
- private Engine engine;
- private GPS GPS;
- public Car getResult() {
- return new Car(carType, seats, engine, GPS);
- }
- }
創(chuàng)建汽車操作手冊builder:
- @Data
- public class ManualBuilder implements Builder {
- private CarType carType;
- private int seats;
- private Engine engine;
- private GPS GPS;
- public Manual getResult() {
- return new Manual(carType, seats, engine, GPS);
- }
- }
創(chuàng)建一個(gè)builder管理器:
- public class Director {
- public void constructSportsCar(Builder builder) {
- builder.setCarType(CarType.SPORT);
- builder.setSeats(2);
- builder.setEngine(new Engine(3.0, 0));
- builder.setGPS(new GPS("上海東方明珠塔到上海動物園"));
- }
- public void constructOrdinaryCar(Builder builder) {
- builder.setCarType(CarType.ORDINARY);
- builder.setSeats(4);
- builder.setEngine(new Engine(2.0, 0));
- builder.setGPS(new GPS("上海東方明珠塔到上海動物園"));
- }
- }
測試類:
- @RunWith(SpringRunner.class)
- @SpringBootTest(classes = TestApplication.class)
- public class TestDemo {
- @Test
- public void test() {
- Director director = new Director();
- //生產(chǎn)跑車
- CarBuilder carBuilder = new CarBuilder();
- director.constructSportsCar(carBuilder);
- Car sportCar = carBuilder.getResult();
- sportCar.detail();
- System.out.println("-----------------------------------");
- //生產(chǎn)普通汽車
- director.constructOrdinaryCar(carBuilder);
- Car ordinaryCar = carBuilder.getResult();
- ordinaryCar.detail();
- System.out.println("-----------------------------------");
- //生產(chǎn)汽車操作手冊
- ManualBuilder manualBuilder = new ManualBuilder();
- director.constructOrdinaryCar(manualBuilder);
- Manual manual = manualBuilder.getResult();
- manual.detail();
- System.out.println("-----------------------------------");
- }
- }
結(jié)果:
- this is SPORT car
- the seats is :2
- the engine is :Engine(volume=3.0, mileage=0.0)
- this GPS is :GPS(route=上海東方明珠塔到上海動物園)
- -----------------------------------
- this is ORDINARY car
- the seats is :4
- the engine is :Engine(volume=2.0, mileage=0.0)
- this GPS is :GPS(route=上海東方明珠塔到上海動物園)
- -----------------------------------
- this Manual ORDINARY car
- the Manual seats is :4
- the Manual engine is :Engine(volume=2.0, mileage=0.0)
- this GManual PS is :GPS(route=上海東方明珠塔到上海動物園)
- -----------------------------------
4、分析
建造者模式基本有以下幾個(gè)角色:
如上面兩種方式的結(jié)果顯示,都可以實(shí)現(xiàn)生產(chǎn)汽車和汽車手冊。
結(jié)果沒什么差異。
在沒使用建造者的方式中:
生產(chǎn)汽車的參數(shù)是由客戶端自己指定的,并且需要傳很多的參數(shù),實(shí)際工作中可能需要更多的參數(shù),可能有部分參數(shù)是不需要的。
使用建造者模式
用戶不需要去具體指定多個(gè)參數(shù)了,對于客戶端更友好。
builder:將產(chǎn)品new()提出到builder中,提供產(chǎn)品所擁有的屬性設(shè)置方法,一類產(chǎn)品都可以使用這個(gè)builder進(jìn)行產(chǎn)品創(chuàng)建。
director:作為builder的管理者,主要控制產(chǎn)品的屬性設(shè)置,在這個(gè)類中,具體指定除了可以生產(chǎn)的產(chǎn)品的構(gòu)造,并且對屬性進(jìn)行賦值,最終返回一個(gè)用戶需要的builder。
客戶端調(diào)用只需要創(chuàng)建需要的產(chǎn)品類型builder,通過管理者director對builder進(jìn)行屬性設(shè)置,最終客戶端通過調(diào)用builder的方法獲取最終需要的產(chǎn)品。
極大程度減少并優(yōu)化的客戶端的代碼,同時(shí)由管理者director限制了產(chǎn)品的種類。
從擴(kuò)展層層面看:
未使用建造者:增加對應(yīng)的產(chǎn)品類,客戶端直接new。
使用建造者模式:增加builder,并且在director增加可創(chuàng)建的產(chǎn)品的builder構(gòu)造。
5、總結(jié)
最后總結(jié)下上面例子中使用抽象工廠方法的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
1)遵守單一原則。
2)不同產(chǎn)品,可復(fù)用相同的產(chǎn)品創(chuàng)建流程。
3)簡化客戶端調(diào)用方式。去除多參構(gòu)造的方式。
4)分步驟創(chuàng)建對象。
缺點(diǎn):
增加多個(gè)類,代碼復(fù)雜度增加。