写在前面
不知道,你在工作中有没有使用过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();
}
@Override
public void setBrand() {
phone.setBrand("Apple");
}
@Override
public void setOs() {
phone.setOs("IOS");
}
@Override
public void setRamSize() {
phone.setRamSize(2);
}
@Override
public void setPrice() {
phone.setPrice(6666.66);
}
@Override
public Phone getPhone() {
return phone;
}
}
/**
* 小米手机建造者
*/
public class MiPhoneBuilder implements PhoneBuilder {
private Phone phone;
public MiPhoneBuilder() {
phone = new Phone();
}
@Override
public void setBrand() {
phone.setBrand("Xiao Mi");
}
@Override
public void setOs() {
phone.setOs("Android");
}
@Override
public void setRamSize() {
phone.setRamSize(8);
}
@Override
public void setPrice() {
phone.setPrice(1999.99);
}
@Override
public 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 建造者模式 。