设计模式系列之建造者模式

开发 前端
建造者模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。

[[377314]]

本文转载自微信公众号「狼王编程」,作者狼王 。转载本文请联系狼王编程公众号。  

 1、概述

建造者模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。

2、适用场景

1)避免重叠构造函数,例如:一个类有很多的属性,这时候的构造方法需要传递很多的参数,为了避免这样,会重新写一个相对参数较少的构造方法,但是仍然需要对其他参数进行赋默认值。

2)当需要创建不同的产品类型,此处指比较接近的产品类型,则可以在顶层builder中包含大部分产品属性的赋值方法。

3、实例

有以下场景,我们分别使用常规方式和建造者模式实现:

  1. 当前有一个汽车工厂,可以生产的汽车类型有ordinary,sport。 
  2. 除了汽车之外,同样可以生产汽车的操作手册manual。 
  3. 汽车有以下的属性: 
  4. 1、type 类型 
  5. 2、seats 座位数 
  6. 3、engine 引擎 
  7. 4、GPS 导航 
  8.  
  9. 分别实现汽车和手册的生产过程 

不使用建造者模式

分别创建车car和手册manual,以及其内部的属性,当前例子中车和手册是相同的属性。

  1. @Data 
  2. public class Car { 
  3.  
  4.     private CarType type; 
  5.  
  6.     private int seats; 
  7.  
  8.     private Engine engine; 
  9.  
  10.     private GPS GPS; 
  11.  
  12.     public Car(CarType type, int seats, Engine engine, com.cloud.bssp.designpatterns.builder.withouttdesign.entity.GPS GPS) { 
  13.         this.type = type; 
  14.         this.seats = seats; 
  15.         this.engine = engine; 
  16.         this.GPS = GPS; 
  17.     } 
  18.  
  19.     public void detail() { 
  20.         System.out.println("this is " + type + " car"); 
  21.         System.out.println("the seats is :" + seats); 
  22.         System.out.println("the engine is :" + engine); 
  23.         System.out.println("this GPS is :" + GPS); 
  24.     } 
  1. @Data 
  2. public class Manual { 
  3.  
  4.     private CarType type; 
  5.  
  6.     private int seats; 
  7.  
  8.     private Engine engine; 
  9.  
  10.     private GPS GPS; 
  11.  
  12.     public Manual(CarType type, int seats, Engine engine, com.cloud.bssp.designpatterns.builder.withouttdesign.entity.GPS GPS) { 
  13.         this.type = type; 
  14.         this.seats = seats; 
  15.         this.engine = engine; 
  16.         this.GPS = GPS; 
  17.     } 
  18.  
  19.     public void detail() { 
  20.         System.out.println("this is " + type + " car"); 
  21.         System.out.println("the seats is :" + seats); 
  22.         System.out.println("the engine is :" + engine); 
  23.         System.out.println("this GPS is :" + GPS); 
  24.     } 
  1. public enum CarType { 
  2.     SPORT,ORDINARY; 
  1. /** 
  2.  * 汽车引擎 
  3.  */ 
  4. @Data 
  5. public class Engine { 
  6.  
  7.     /** 
  8.      * 排量 
  9.      */ 
  10.     private final double volume; 
  11.     /** 
  12.      * 里程 
  13.      */ 
  14.     private double mileage; 
  15.  
  16.     public Engine(double volume, double mileage) { 
  17.         this.volume = volume; 
  18.         this.mileage = mileage; 
  19.     } 
  1. @Data 
  2. public class GPS { 
  3.     /** 
  4.      * 路线 
  5.      */ 
  6.     private String route; 
  7.  
  8.     public GPS(String route) { 
  9.         this.route = route; 
  10.     } 

测试类:

 

  1. /** 
  2.  * 客户端 
  3.  */ 
  4. @RunWith(SpringRunner.class) 
  5. @SpringBootTest(classes = TestApplication.class) 
  6. public class TestDemo { 
  7.  
  8.     @Test 
  9.     public void test() { 
  10.         //生产跑车 
  11.         Car sportCar = new Car( 
  12.                 CarType.SPORT, 
  13.                 2, 
  14.                 new Engine(3.0, 0), 
  15.                 new GPS("上海东方明珠塔到上海动物园"
  16.         ); 
  17.         sportCar.detail(); 
  18.         System.out.println("----------------------------------------"); 
  19.         //生产普通汽车 
  20.         Car ordinaryCar = new Car( 
  21.                 CarType.ORDINARY, 
  22.                 4, 
  23.                 new Engine(2.0, 0), 
  24.                 new GPS("上海东方明珠塔到上海动物园"
  25.         ); 
  26.         ordinaryCar.detail(); 
  27.         System.out.println("----------------------------------------"); 
  28.         //生产汽车操作手册 
  29.         Manual manual = new Manual( 
  30.                 CarType.ORDINARY, 
  31.                 4, 
  32.                 new Engine(2.0, 0), 
  33.                 new GPS("上海东方明珠塔到上海动物园"
  34.         ); 
  35.         manual.detail(); 
  36.         System.out.println("----------------------------------------"); 
  37.     } 

结果:

  1. this is SPORT car 
  2. the seats is :2 
  3. the engine is :Engine(volume=3.0, mileage=0.0) 
  4. this GPS is :GPS(route=上海东方明珠塔到上海动物园) 
  5. ---------------------------------------- 
  6. this is ORDINARY car 
  7. the seats is :4 
  8. the engine is :Engine(volume=2.0, mileage=0.0) 
  9. this GPS is :GPS(route=上海东方明珠塔到上海动物园) 
  10. ---------------------------------------- 
  11. this is ORDINARY car 
  12. the seats is :4 
  13. the engine is :Engine(volume=2.0, mileage=0.0) 
  14. this GPS is :GPS(route=上海东方明珠塔到上海动物园) 
  15. ---------------------------------------- 

使用建造者模式实现

使用建造者模式后,代码要比上面的方法多了不少:

创建顶层Builder

  1. public interface Builder { 
  2.  
  3.     void setCarType(CarType carType); 
  4.  
  5.     void setSeats(int seats); 
  6.  
  7.     void setEngine(Engine engine); 
  8.  
  9.     void setGPS(GPS gps); 

创建实体类,与上面是相同的,这里不重复了。

创建car的builder:

  1. @Data 
  2. public class CarBuilder implements Builder { 
  3.  
  4.     private CarType carType; 
  5.  
  6.     private int seats; 
  7.  
  8.     private Engine engine; 
  9.  
  10.     private GPS GPS; 
  11.  
  12.     public Car getResult() { 
  13.         return new Car(carType, seats, engine, GPS); 
  14.     } 

创建汽车操作手册builder:

  1. @Data 
  2. public class ManualBuilder implements Builder { 
  3.  
  4.     private CarType carType; 
  5.  
  6.     private int seats; 
  7.  
  8.     private Engine engine; 
  9.  
  10.     private GPS GPS; 
  11.  
  12.     public Manual getResult() { 
  13.         return new Manual(carType, seats, engine, GPS); 
  14.     } 

创建一个builder管理器:

  1. public class Director { 
  2.  
  3.     public void constructSportsCar(Builder builder) { 
  4.         builder.setCarType(CarType.SPORT); 
  5.         builder.setSeats(2); 
  6.         builder.setEngine(new Engine(3.0, 0)); 
  7.         builder.setGPS(new GPS("上海东方明珠塔到上海动物园")); 
  8.     } 
  9.  
  10.     public void constructOrdinaryCar(Builder builder) { 
  11.         builder.setCarType(CarType.ORDINARY); 
  12.         builder.setSeats(4); 
  13.         builder.setEngine(new Engine(2.0, 0)); 
  14.         builder.setGPS(new GPS("上海东方明珠塔到上海动物园")); 
  15.     } 

测试类:

  1. @RunWith(SpringRunner.class) 
  2. @SpringBootTest(classes = TestApplication.class) 
  3. public class TestDemo { 
  4.  
  5.     @Test 
  6.     public void test() { 
  7.         Director director = new Director(); 
  8.         //生产跑车 
  9.         CarBuilder carBuilder = new CarBuilder(); 
  10.         director.constructSportsCar(carBuilder); 
  11.         Car sportCar = carBuilder.getResult(); 
  12.         sportCar.detail(); 
  13.         System.out.println("-----------------------------------"); 
  14.  
  15.         //生产普通汽车 
  16.         director.constructOrdinaryCar(carBuilder); 
  17.         Car ordinaryCar = carBuilder.getResult(); 
  18.         ordinaryCar.detail(); 
  19.         System.out.println("-----------------------------------"); 
  20.  
  21.         //生产汽车操作手册 
  22.         ManualBuilder manualBuilder = new ManualBuilder(); 
  23.         director.constructOrdinaryCar(manualBuilder); 
  24.         Manual manual = manualBuilder.getResult(); 
  25.         manual.detail(); 
  26.         System.out.println("-----------------------------------"); 
  27.     } 

结果:

  1. this is SPORT car 
  2. the seats is :2 
  3. the engine is :Engine(volume=3.0, mileage=0.0) 
  4. this GPS is :GPS(route=上海东方明珠塔到上海动物园) 
  5. ----------------------------------- 
  6. this is ORDINARY car 
  7. the seats is :4 
  8. the engine is :Engine(volume=2.0, mileage=0.0) 
  9. this GPS is :GPS(route=上海东方明珠塔到上海动物园) 
  10. ----------------------------------- 
  11. this Manual ORDINARY car 
  12. the Manual seats is :4 
  13. the Manual engine is :Engine(volume=2.0, mileage=0.0) 
  14. this GManual PS is :GPS(route=上海东方明珠塔到上海动物园) 
  15. ----------------------------------- 

4、分析

建造者模式基本有以下几个角色:

如上面两种方式的结果显示,都可以实现生产汽车和汽车手册。

结果没什么差异。

在没使用建造者的方式中:

生产汽车的参数是由客户端自己指定的,并且需要传很多的参数,实际工作中可能需要更多的参数,可能有部分参数是不需要的。

使用建造者模式

用户不需要去具体指定多个参数了,对于客户端更友好。

builder:将产品new()提出到builder中,提供产品所拥有的属性设置方法,一类产品都可以使用这个builder进行产品创建。

director:作为builder的管理者,主要控制产品的属性设置,在这个类中,具体指定除了可以生产的产品的构造,并且对属性进行赋值,最终返回一个用户需要的builder。

客户端调用只需要创建需要的产品类型builder,通过管理者director对builder进行属性设置,最终客户端通过调用builder的方法获取最终需要的产品。

极大程度减少并优化的客户端的代码,同时由管理者director限制了产品的种类。

从扩展层层面看:

未使用建造者:增加对应的产品类,客户端直接new。

使用建造者模式:增加builder,并且在director增加可创建的产品的builder构造。

5、总结

最后总结下上面例子中使用抽象工厂方法的优缺点:

优点:

1)遵守单一原则。

2)不同产品,可复用相同的产品创建流程。

3)简化客户端调用方式。去除多参构造的方式。

4)分步骤创建对象。

缺点:

增加多个类,代码复杂度增加。

 

责任编辑:武晓燕 来源: 狼王编程
相关推荐

2020-10-20 13:33:00

建造者模式

2021-10-26 00:21:19

设计模式建造者

2021-07-08 11:28:43

观察者模式设计

2021-04-14 09:02:22

模式 设计建造者

2020-11-05 09:38:07

中介者模式

2011-07-14 14:46:46

设计模式

2020-10-26 08:45:39

观察者模式

2021-06-09 08:53:34

设计模式策略模式工厂模式

2012-01-13 15:59:07

2024-02-19 08:38:34

建造者模式Android设计模式

2013-11-26 17:09:57

Android设计模式

2020-12-01 07:16:05

重学设计模式

2021-03-05 07:57:41

设计模式桥接

2021-06-22 15:27:13

设计模式迭代器模式Java

2022-01-29 22:12:35

前端模式观察者

2012-05-16 17:15:04

Java设计模式

2021-02-18 08:39:28

设计模式场景

2020-05-25 10:20:19

享元模式场景

2010-05-06 08:44:37

调解者模式

2021-05-11 08:54:59

建造者模式设计
点赞
收藏

51CTO技术栈公众号