本文转载自微信公众号「狼王编程」,作者狼王 。转载本文请联系狼王编程公众号。
1、概述
建造者模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。
2、适用场景
1)避免重叠构造函数,例如:一个类有很多的属性,这时候的构造方法需要传递很多的参数,为了避免这样,会重新写一个相对参数较少的构造方法,但是仍然需要对其他参数进行赋默认值。
2)当需要创建不同的产品类型,此处指比较接近的产品类型,则可以在顶层builder中包含大部分产品属性的赋值方法。
3、实例
有以下场景,我们分别使用常规方式和建造者模式实现:
- 当前有一个汽车工厂,可以生产的汽车类型有ordinary,sport。
- 除了汽车之外,同样可以生产汽车的操作手册manual。
- 汽车有以下的属性:
- 1、type 类型
- 2、seats 座位数
- 3、engine 引擎
- 4、GPS 导航
- 分别实现汽车和手册的生产过程
不使用建造者模式
分别创建车car和手册manual,以及其内部的属性,当前例子中车和手册是相同的属性。
- @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() {
- //生产跑车
- Car sportCar = new Car(
- CarType.SPORT,
- 2,
- new Engine(3.0, 0),
- new GPS("上海东方明珠塔到上海动物园")
- );
- sportCar.detail();
- System.out.println("----------------------------------------");
- //生产普通汽车
- Car ordinaryCar = new Car(
- CarType.ORDINARY,
- 4,
- new Engine(2.0, 0),
- new GPS("上海东方明珠塔到上海动物园")
- );
- ordinaryCar.detail();
- System.out.println("----------------------------------------");
- //生产汽车操作手册
- Manual manual = new Manual(
- CarType.ORDINARY,
- 4,
- new Engine(2.0, 0),
- new GPS("上海东方明珠塔到上海动物园")
- );
- manual.detail();
- System.out.println("----------------------------------------");
- }
- }
结果:
- 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=上海东方明珠塔到上海动物园)
- ----------------------------------------
使用建造者模式实现
使用建造者模式后,代码要比上面的方法多了不少:
创建顶层Builder
- public interface Builder {
- void setCarType(CarType carType);
- void setSeats(int seats);
- void setEngine(Engine engine);
- void setGPS(GPS gps);
- }
创建实体类,与上面是相同的,这里不重复了。
创建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);
- }
- }
创建汽车操作手册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);
- }
- }
创建一个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();
- //生产跑车
- CarBuilder carBuilder = new CarBuilder();
- director.constructSportsCar(carBuilder);
- Car sportCar = carBuilder.getResult();
- sportCar.detail();
- System.out.println("-----------------------------------");
- //生产普通汽车
- director.constructOrdinaryCar(carBuilder);
- Car ordinaryCar = carBuilder.getResult();
- ordinaryCar.detail();
- System.out.println("-----------------------------------");
- //生产汽车操作手册
- ManualBuilder manualBuilder = new ManualBuilder();
- director.constructOrdinaryCar(manualBuilder);
- Manual manual = manualBuilder.getResult();
- manual.detail();
- System.out.println("-----------------------------------");
- }
- }
结果:
- 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、分析
建造者模式基本有以下几个角色:
如上面两种方式的结果显示,都可以实现生产汽车和汽车手册。
结果没什么差异。
在没使用建造者的方式中:
生产汽车的参数是由客户端自己指定的,并且需要传很多的参数,实际工作中可能需要更多的参数,可能有部分参数是不需要的。
使用建造者模式
用户不需要去具体指定多个参数了,对于客户端更友好。
builder:将产品new()提出到builder中,提供产品所拥有的属性设置方法,一类产品都可以使用这个builder进行产品创建。
director:作为builder的管理者,主要控制产品的属性设置,在这个类中,具体指定除了可以生产的产品的构造,并且对属性进行赋值,最终返回一个用户需要的builder。
客户端调用只需要创建需要的产品类型builder,通过管理者director对builder进行属性设置,最终客户端通过调用builder的方法获取最终需要的产品。
极大程度减少并优化的客户端的代码,同时由管理者director限制了产品的种类。
从扩展层层面看:
未使用建造者:增加对应的产品类,客户端直接new。
使用建造者模式:增加builder,并且在director增加可创建的产品的builder构造。
5、总结
最后总结下上面例子中使用抽象工厂方法的优缺点:
优点:
1)遵守单一原则。
2)不同产品,可复用相同的产品创建流程。
3)简化客户端调用方式。去除多参构造的方式。
4)分步骤创建对象。
缺点:
增加多个类,代码复杂度增加。