写在前面
不知道,你在工作中有没有使用过lombok,如果你使用过,不知道你有没有使用过其中的@Builder
注解,其就会帮我们生成建造者设计模式相关的代码,本文就一起来看下吧!
1:介绍
1.1:什么时候使用建造者设计模式
当一个对象的属性很多,并且在不同的场景下对象创建时需要初始化的属性不同时,可以考虑使用该设计模式,否则就需要创建大量的构造函数,造成代码的臃肿和难以维护,并且对于使用者来说,到底选择哪个构造函数来初始化也会比较麻烦。
1.2:UML类图
工厂方法设计模式,包含如下元素:
1:产品待创建的对象
2:抽象构造者定义创建产品需要设置的属性
3:具体构造者具体产品的构造者,继承抽象构造者,负责设置具体的信息
4:导演使用构造者来创建产品
如下图:
2:实例
源码 。
2.1:场景
按照不同的需求创建手机对象。
2.2:程序
- 创建产品类
@Data // 可自动生成get、set、toString方法
public class Phone {/*** 品牌*/private String brand;/*** 操作系统*/private String os;/*** 内存大小, Unit: GB*/private Integer ramSize;/*** 售价*/private Double price;
}
- 创建抽象构造器
/*** 手机抽象建造者的接口*/
public interface PhoneBuilder {/*** 设置品牌*/void setBrand();/*** 设置操作系统*/void setOs();/*** 设置内存大小*/void setRamSize();/*** 设置售价*/void setPrice();/*** 获取 Phone 实例* @return*/Phone getPhone();
}
- 创建两个具体的构造器
分别创建预定好的苹果手机,和小米手机:
/*** 苹果手机建造者*/
public class IPhoneBuilder implements PhoneBuilder{private Phone phone;public IPhoneBuilder() {phone = new Phone();}@Overridepublic void setBrand() {phone.setBrand("Apple");}@Overridepublic void setOs() {phone.setOs("IOS");}@Overridepublic void setRamSize() {phone.setRamSize(2);}@Overridepublic void setPrice() {phone.setPrice(6666.66);}@Overridepublic Phone getPhone() {return phone;}
}/*** 小米手机建造者*/
public class MiPhoneBuilder implements PhoneBuilder {private Phone phone;public MiPhoneBuilder() {phone = new Phone();}@Overridepublic void setBrand() {phone.setBrand("Xiao Mi");}@Overridepublic void setOs() {phone.setOs("Android");}@Overridepublic void setRamSize() {phone.setRamSize(8);}@Overridepublic void setPrice() {phone.setPrice(1999.99);}@Overridepublic Phone getPhone() {return phone;}
}
- 创建导演类
/*** 导演: 负责指定手机建造流程*/
public class PhoneDirector {/*** 导演指挥建造者完成手机的建造工作* @param phoneBuilder*/public void construct(PhoneBuilder phoneBuilder) {phoneBuilder.setBrand();phoneBuilder.setOs();phoneBuilder.setRamSize();phoneBuilder.setPrice();}
}
- 测试
@Test
public void origin() {// 1. 创建一个导演PhoneDirector phoneDirector = new PhoneDirector();/***** 2. 建造 iPhone 手机 *****/// 2a. 创建一个 iPhone建造者IPhoneBuilder iPhoneBuilder = new IPhoneBuilder();// 2b. 导演指导 iPhone建造者 来建造一个iPhone的实例phoneDirector.construct(iPhoneBuilder);// 2c. 从 iPhone建造者 中获取实例Phone iPhone = iPhoneBuilder.getPhone();System.out.println(iPhone);/***** 3. 建造 Xiao Mi Phone 手机 *****/// 3a. 创建一个 MiPhone建造者MiPhoneBuilder miPhoneBuilder = new MiPhoneBuilder();// 3b. 导演指导 MiPhone建造者 来建造一个MiPhone的实例phoneDirector.construct(miPhoneBuilder);// 3c. 从 MiPhone建造者 中获取实例Phone miPhone = miPhoneBuilder.getPhone();System.out.println(miPhone);}
运行:
Phone(brand=Apple, os=IOS, ramSize=2, price=6666.66)
Phone(brand=Xiao Mi, os=Android, ramSize=8, price=1999.99)Process finished with exit code 0
需要注意到,这里客户端虽然不需要去设置对象的各种属性信息了,但是仅仅适用于要设置的属性都是确定的,并且要设置的属性值也是确定的场景,如果是要设置哪些属性是不确定,要设置的属性值也是不确定的话,这种方式明显就不使用了,怎么做呢?可以创建对应的构造函数,直接使用构造函数创建,但是可能需要创建非常多的构造函数,会让代码变的臃肿且难以维护。也可以调用无参构造函数,然后分别调用对应的setXxx方法,但是这样程序会变的复杂,且效率低下,因此就有了建造者设计模式的简化版本,这种方式创建一个内部的静态Builder类,之后通过链式调用的方式来设置属性,最终调用build,build内会调用对象的全部参数的构造函数,从而完成对象创建,使用简化版本的建造者设计模式修改Phone类如下:
/*** 手机*/
@ToString
public class Phone {/*** 品牌*/private String brand;/*** 操作系统*/private String os;/*** 内存大小, Unit: GB*/private Integer ramSize;/*** 售价*/private Double price;/*** 提供一个静态方法以方便创建一个Phone建造者实例* @return*/public static Phone.PhoneBuilder builder() {return new Phone.PhoneBuilder();}/*** 提供一个Phone的全参构造器以供建造者Builder来建造Phone实例* @param brand* @param os* @param ramSize* @param price*/public Phone(String brand, String os, Integer ramSize, Double price) {this.brand = brand;this.os = os;this.ramSize = ramSize;this.price = price;}/*** 静态内部类: Phone Builder 建造者*/public static class PhoneBuilder {private String brand;private String os;private Integer ramSize;private Double price;/*** Builder 建造者构造器*/public PhoneBuilder() {}public Phone.PhoneBuilder brand(String brand) {this.brand = brand;return this;}public Phone.PhoneBuilder os(String os) {this.os = os;return this;}public Phone.PhoneBuilder ramSize(Integer ramSize) {this.ramSize = ramSize;return this;}public Phone.PhoneBuilder price(Double price) {this.price = price;return this;}/*** 建造者通过 Phone的全参构造器 来构造 Phone 实例* @return*/public Phone build() {return new Phone(brand, os, ramSize, price);}}
}
测试:
@Test
public void simplify() {dongshi.daddy.builder.simplify.Phone P40Pro= dongshi.daddy.builder.simplify.Phone.builder() // 通过产品的静态方法获取建造者.brand("华为") // "客户"(调用方)充当了Director这个角色.os("鸿蒙").ramSize(12).price(9999.99).build(); // 获得建造完成的产品System.out.println(P40Pro);
}
输出:
Phone(brand=华为, os=鸿蒙, ramSize=12, price=9999.99)Process finished with exit code 0
这种简化版本的构造者设计模式其实就是我们在工作中经常用的lombok 的@Builder注解,如下:
@Builder
@ToString
public class PhoneWithLombok {/*** 品牌*/private String brand;/*** 操作系统*/private String os;/*** 内存大小, Unit: GB*/private Integer ramSize;/*** 售价*/private Double price;
}
程序是不是简洁多了,工作中用起来吧!
测试:
@Test
public void simplifyWithLombok() {PhoneWithLombok P40Pro= PhoneWithLombok.builder() // 通过产品的静态方法获取建造者.brand("华为lombok") // "客户"(调用方)充当了Director这个角色.os("lombok 操作系统").ramSize(12).price(9999.99).build(); // 获得建造完成的产品System.out.println(P40Pro);
}
输出:
PhoneWithLombok(brand=华为lombok, os=lombok 操作系统, ramSize=12, price=9999.99)Process finished with exit code 0
写在后面
参考文章列表
GoF设计模式(五):Builder Pattern 建造者模式 。