1、建造者模式
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
现在有一个需求:用户买电脑
@Data
@Accessors(chain = true)
class Compute {
private String cpu;
private String gpu;
private String memory;
private String hd;
}
-------------------------------------------时空线-----------------------------------------------
public class BuilderTest {
public static void main(String[] args) {
Compute compute = new Compute()
.setCpu("i7-10700")
.setGpu("3080 Ti")
.setMemory("32G")
.setHd("1T");
System.out.println(compute);
}
}
看一下这样设计的问题是什么:
- 对象实例化的时候就必须要为每一个属性赋值,比较麻烦
- 相当于你去配电脑,卖家把所有组件都给你摆出来,让你自己亲自动手组装
- 这样有可能会不按正确步骤组装对象,或缺少了某一个组件
- 违反了迪米特法则
第一次改进:
作者额外添加一个功能:直接封装装配电脑的工作。需要专门创建一个ComputeBuilder
类,来专门负责组装电脑的过程
@Data
@Accessors(chain = true)
class Compute {
private String cpu;
private String gpu;
private String memory;
private String hd;
}
// 电脑构建者类,并且必须关联一个产品
class ComputeBuilder {
private Compute compute = new Compute();
// 构建方法
public Compute build() {
return compute.setCpu("i7-10700")
.setGpu("3080 Ti")
.setMemory("32G")
.setHd("1T");
}
}
-------------------------------------------时空线-----------------------------------------------------
public class BuilderApp {
public static void main(String[] args) {
// 创建一个建造者
ComputeBuilder cb = new ComputeBuilder();
// 创建电脑
// 学习 (cpu好 + 内存 )
Computer c = cb.build();
System.out.println(c);
// 玩游戏(显卡好)
Computer c2 = cb.build();
System.out.println(c2);
// 娱乐、办公
Computer c3 = cb.build();
System.out.println(c3);
}
}
这样设计的好处是:
- 由建造者隐藏了创建对象的复杂过程
但缺点是什么?
- 封装的太狠! 无论客户端是什么需求,客户端没办法选择配置! 都是由服务器端定死的配置!!
我们需要的效果是:
1、针对于不同需求,有不同的建造者。
2、客户端仍然不知道建造电脑的过程。
第二次改进:
@Data
@Accessors(chain = true)
class Computer {
private String cpu;
private String gpu;
private String memory;
private String hd;
}
class AdvancedComputerBuilder {
private Computer c = new Computer();
// 构建方法
public Computer build() {
return c.setCpu("i7-10700")
.setGpu("4090 Ti")
.setMemory("32G")
.setHd("2T");
}
}
class MiddleComputerBuilder {
private Computer c = new Computer();
public Computer build() {
return c.setCpu("i7-10700")
.setGpu("3080 Ti")
.setMemory("16G")
.setHd("1T");
}
}
class LowComputerBuilder {
private Computer c = new Computer();
public Computer build() {
return c.setCpu("i7-10700")
.setGpu("1650 Ti")
.setMemory("16G")
.setHd("512G");
}
}
-----------------------------------------------------------------------------------------------------------
public class BuilderApp {
public static void main(String[] args) {
AdvancedComputerBuilder acb = new AdvancedComputerBuilder();
MiddleComputerBuilder mcb = new MiddleComputerBuilder();
LowComputerBuilder lcb = new LowComputerBuilder();
// 游戏
Computer c3 = acb.build();
System.out.println(c3);
// 学习
Computer c = mcb.build();
System.out.println(c);
// 办公、娱乐
Computer c2 = lcb.build();
System.out.println(c2);
}
}
虽然满足了上述需求,但还是有问题
1、无论是高 中 低哪一种配置的电脑,配置的过程都是一样的,只不过细节不一样。 既然有重复,那就有了坏味道。
2、这些重复的安装步骤,不稳定,万一哪一个建造者中漏了某一个步骤,那么生产出来的产品就是不合格的,但是编译器却不会报错
改进一下:
1、稳定建造电脑的过程, 定义一个建造者接口
2、让所有具体建造者实现这个接口,那么所有的具体建造者,就必须实现这个接口中的所有方法,就不会遗漏某一个步骤。
第三次改进:
package day01.designPattern;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
class Computer {
private String cpu;
private String gpu;
private String memory;
private String hd;
}
interface IcomputeBuilder {
IcomputeBuilder cpu();
IcomputeBuilder gpu();
IcomputeBuilder memory();
IcomputeBuilder hd();
Computer builder();
}
class AdvancedComputerBuilder implements IcomputeBuilder{
private Computer compute = new Computer();
@Override
public IcomputeBuilder cpu() {
compute.setCpu("i7-10700");
return this;
}
@Override
public IcomputeBuilder gpu() {
compute.setGpu("4090 Ti");
return this;
}
@Override
public IcomputeBuilder memory() {
compute.setMemory("32G");
return this;
}
@Override
public IcomputeBuilder hd() {
compute.setHd("2T");
return this;
}
// 构建方法
public Computer builder() {
return compute;
}
}
class MiddleComputerBuilder implements IcomputeBuilder{
private Computer compute = new Computer();
@Override
public IcomputeBuilder cpu() {
compute.setCpu("i7-10700");
return this;
}
@Override
public IcomputeBuilder gpu() {
compute.setGpu("3080 Ti");
return this;
}
@Override
public IcomputeBuilder memory() {
compute.setMemory("16G");
return this;
}
@Override
public IcomputeBuilder hd() {
compute.setHd("1T");
return this;
}
// 构建方法
public Computer builder() {
return compute;
}
}
class LowComputerBuilder implements IcomputeBuilder{
private Computer compute = new Computer();
@Override
public IcomputeBuilder cpu() {
compute.setCpu("i5-10700");
return this;
}
@Override
public IcomputeBuilder gpu() {
compute.setGpu("1650 Ti");
return this;
}
@Override
public IcomputeBuilder memory() {
compute.setMemory("8G");
return this;
}
@Override
public IcomputeBuilder hd() {
compute.setHd("1T");
return this;
}
// 构建方法
public Computer builder() {
return compute;
}
}
----------------------------------------------------------------------------------------------------------
public class BuilderApp {
public static void main(String[] args) {
AdvancedComputerBuilder acb = new AdvancedComputerBuilder();
acb.cpu().gpu().memory().hd();
Computer c = acb.builder();
System.out.println(c);
MiddleComputerBuilder mcb = new MiddleComputerBuilder();
mcb.cpu().gpu().memory().hd();
Computer c2 = mcb.builder();
System.out.println(c2);
LowComputerBuilder lcb = new LowComputerBuilder();
lcb.cpu().gpu().memory().hd();
Computer c3 = lcb.builder();
System.out.println(c3);
}
/**
* Computer(cpu=i7-10700, gpu=4090 Ti, memory=32G, hd=2T)
* Computer(cpu=i7-10700, gpu=3080 Ti, memory=16G, hd=1T)
* Computer(cpu=i5-10700, gpu=1650 Ti, memory=8G, hd=1T)
*/
}
我们看这样进行构建的优点:
建造者类中的建造过程是稳定的,不会漏掉某一步。这样当客户端想扩展建造者时,也不会漏掉某一步
缺点:
1、如果有多个建造者,代码依然会有重复
2、现在又变成了客户端自己配置电脑,又违反了迪米特法则。
(这相当于,你去电脑城配电脑,虽然不用你亲自组装电脑,但是你必须指挥那个装机boy,下一步该干啥,下一步该干啥,虽然隐藏细节,但是还是要指挥)
而我们想要的是连指挥过程也要隐藏起来
第四次改进:
@Data
@Accessors(chain = true)
class Computer {
private String cpu;
private String gpu;
private String memory;
private String hd;
}
interface IComputeBuilder {
IComputeBuilder cpu();
IComputeBuilder gpu();
IComputeBuilder memory();
IComputeBuilder hd();
Computer builder();
}
// 隐藏指挥命令的细节
class Director {
public Computer build(IComputeBuilder computerBuilder) {
// 指挥builder进行组装
return computerBuilder.cpu().gpu().memory().hd().builder();
}
}
class AdvancedComputerBuilder implements IComputeBuilder{
private Computer compute = new Computer();
@Override
public IComputeBuilder cpu() {
compute.setCpu("i7-10700");
return this;
}
@Override
public IComputeBuilder gpu() {
compute.setGpu("4090 Ti");
return this;
}
@Override
public IComputeBuilder memory() {
compute.setMemory("32G");
return this;
}
@Override
public IComputeBuilder hd() {
compute.setHd("2T");
return this;
}
// 构建方法
public Computer builder() {
return compute;
}
}
class MiddleComputerBuilder implements IComputeBuilder{
private Computer compute = new Computer();
@Override
public IComputeBuilder cpu() {
compute.setCpu("i7-10700");
return this;
}
@Override
public IComputeBuilder gpu() {
compute.setGpu("3080 Ti");
return this;
}
@Override
public IComputeBuilder memory() {
compute.setMemory("16G");
return this;
}
@Override
public IComputeBuilder hd() {
compute.setHd("1T");
return this;
}
// 构建方法
public Computer builder() {
return compute;
}
}
class LowComputerBuilder implements IComputeBuilder{
private Computer compute = new Computer();
@Override
public IComputeBuilder cpu() {
compute.setCpu("i5-10700");
return this;
}
@Override
public IComputeBuilder gpu() {
compute.setGpu("1650 Ti");
return this;
}
@Override
public IComputeBuilder memory() {
compute.setMemory("8G");
return this;
}
@Override
public IComputeBuilder hd() {
compute.setHd("1T");
return this;
}
// 构建方法
public Computer builder() {
return compute;
}
}
-------------------------------------------------------------------------------------------------------
public class BuilderApp {
public static void main(String[] args) {
// 创建指挥者
Director director = new Director();
//游戏
AdvancedComputerBuilder acb = new AdvancedComputerBuilder();// 创建一个建造者
Computer c = director.build(acb);// 由指挥者进行指挥
System.out.println(c);
//开发
MiddleComputerBuilder mcb = new MiddleComputerBuilder();
Computer c2 = director.build(mcb);
System.out.println(c2);
//日常办公
LowComputerBuilder lcb = new LowComputerBuilder();
Computer c3 = director.build(acb);
System.out.println(c3);
}
/**
* Computer(cpu=i7-10700, gpu=4090 Ti, memory=32G, hd=2T)
* Computer(cpu=i7-10700, gpu=3080 Ti, memory=16G, hd=1T)
* Computer(cpu=i5-10700, gpu=1650 Ti, memory=8G, hd=1T)
*/
}
至此,建造者设计模式的第一种形态,推演完毕!
优点是:
- 建造者负责建造,指挥者负责指挥,创建对象的过程是稳定的(IComputerBuilder接口负责稳定),创建对象的过程也不会有重复代码(指挥者完成)
- 当需要拓展新的产品时,不需要修改原来的代码,只需要实现构建者接口,然后交给指挥者完成即可,这里就将构造和流程分别由构造者和指挥者进行解耦
- 建造者作为中间层,进行解耦
缺点是,客户端无法灵活控制组件的细节,比如,客户端无法直接指定cpu或者硬盘的型号。
所以才有了建造者模式的第二种形态
当一个类的属性特别多的时候,构造器的参数也就特别多,尤其是构造器的多个参数类型还都一样,那么客户端代码就很容易出错
// 当一个类的属性特别多的时候,构造器的参数也就特别多,尤其是构造器的多个参数类型还都一样。
// 那么客户端代码就很容易出错。
@Data
@Accessors(chain = true)
class Computer {
private String cpu;
private String harddisk;
private String memery;
private String gpu;
private String power;
private String motherboard; // 主板
private String cdrom;
private String fan;
private String netcard;
public Computer() {
}
private Computer(ComputerBuilder cb) {
this.cpu = cb.cpu;
this.harddisk = cb.harddisk;
this.memery = cb.memery;
this.gpu = cb.gpu;
this.power = cb.power;
this.netcard = cb.netcard;
this.motherboard = cb.motherboard;
this.fan = cb.fan;
this.cdrom = cb.cdrom;
}
// 以上的构造器,使用起来确实很麻烦! 也许有小伙伴会这样问,我们可以对构造器进行重载,把参数减少, 事实证明,这也很2....
// 最好的解决方法是使用建造者设计模式的第二种形态
// 1. 建造者必须是产品的一个静态内部类
// 2. 建造者的属性完全与产品的所有属性一致
// 3. 为建造者的每一个属性都添加一个setter方法,且该setter方法必须返回建造者对象本身
public static class ComputerBuilder {
private String cpu;
private String harddisk;
private String memery;
private String gpu;
private String power;
private String motherboard; // 主板
private String cdrom;
private String fan;
private String netcard;
public ComputerBuilder setCpu(String cpu) {
this.cpu = cpu;
return this;
}
public ComputerBuilder setHarddisk(String harddisk) {
this.harddisk = harddisk;
return this;
}
public ComputerBuilder setMemery(String memery) {
this.memery = memery;
return this;
}
public ComputerBuilder setGpu(String gpu) {
this.gpu = gpu;
return this;
}
public ComputerBuilder setPower(String power) {
this.power = power;
return this;
}
public ComputerBuilder setMotherboard(String motherboard) {
this.motherboard = motherboard;
return this;
}
public ComputerBuilder setCdrom(String cdrom) {
this.cdrom = cdrom;
return this;
}
public ComputerBuilder setFan(String fan) {
this.fan = fan;
return this;
}
public ComputerBuilder setNetcard(String netcard) {
this.netcard = netcard;
return this;
}
// 构建方法
public Computer build() {
System.out.println("组装" + cpu + "," + gpu + "," + memery);
return new Computer(this);
}
}
}
public class Demo02 {
public static void main(String[] args) {
Computer.ComputerBuilder cb = new Computer.ComputerBuilder();
Computer c = cb.setMotherboard("华硕主板").setCpu("酷睿8代").setFan("cpu自带的风扇").setHarddisk("500g").setMemery("32G").build();
System.out.println(c);
}
/**
* 组装酷睿8代,null,32G
* Computer(cpu=酷睿8代, harddisk=500g, memery=32G, gpu=null, power=null, motherboard=华硕主板, cdrom=null, fan=cpu自带的风扇, netcard=null)
*/
}
// 建造者设计模式的第二种形态: 参考effective java 第二条2 :当类的属性过多时,就使用建造者设计模式的第二种形态
2、UML类图
3、建造者模式的优缺点
- 优点:
- 创建对象的过程稳定不变,因为有ComputerBuilder接口来稳定过程
- 创建对象的过程只写了一次,没有重复的代码(指挥者完成)
- 当需要扩展指挥者的时候,不用修改之前的代码,这符合了开闭原则
- 缺点:
- 产品必须有共同点,范围有限制。
- 如内部变化复杂,会有很多的建造类。
4、建造者模式与工厂模式的区别
- 工厂模式只负责创建实例,new出对象即可,并不关心里面的属性和构建的过程;
- 建造者模式更加关注构建的过程,通过一些规范的流程、标准,构建合格的产品