建造者模式(Builder Design)
定义
把一个复杂对象的构建和表示分离开,使同样的构建构成可以创建不同的表示(官方的定义)
UML图说明
抽象建造者接口( Builder):抽象定义的建造者(非必须的)
导演角色类(Director): 统一的指挥者角色,去指定建造目标(非必须的)
具体建造者类(ConcreateBuilder): 具体建造的逻辑,可以由有多个,因为每个建造的风格都不一样
产品(Product): 具体的产品,建造的对象
用例说明
小红去电脑城买电脑,也不懂配置,让电脑城老板给他配一台电脑,配置什么的打游戏爽就行了,老板找到员工,按要求组装一台牛逼的电脑。一会小花来这个老板这里,说给她装配一个能办公的电脑就行了,老板又找到员工,提供了另外一套配置。这里可以看出都是买电脑,但是不同的人有不同的配置(但是电脑的组装流程是一样的)。接下来代码演示:
产品(Product),这里指代的电脑(Computer.java)
@Data public class Computer { private String cpu ; // cpu private String disk ; //硬盘 private String motherboard ; // 主板 private String memory ; // 内存 }
抽象建造者接口( Builder)
public interface Builder { // 安装 cpu void createCpu(String cpu) ; // 安装硬盘 void createDisk(String disk) ; // 安装主板 void createMotherBoard(String motherBoard) ; // 安装内存 void createMemory(String memory) ; // 组成电脑 Computer createComputer() ; }
具体建造者类(ConcreateBuilder)
public class ConcreateBuilder implements Builder { private Computer computer = new Computer(); @Override public void createCpu(String cpu) { computer.setCpu(cpu); } @Override public void createDisk(String disk) { computer.setDisk(disk); } @Override public void createMotherBoard(String motherBoard) { computer.setMotherboard(motherBoard); } @Override public void createMemory(String memory) { computer.setMemory(memory); } @Override public Computer createComputer() { return computer; } }
导演角色类(Director)
public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public Computer createComputer(String cpu, String hardDisk, String mainBoard, String memory) { this.builder.createMotherBoard(mainBoard); this.builder.createCpu(cpu) ; this.builder.createMemory(memory); this.builder.createDisk(hardDisk); return builder.createComputer(); } }
测试类
public class Test { public static void main(String[] args) { AssemblerBuilder assemblerBuilder = new AssemblerBuilder(); Director director = new Director(assemblerBuilder); Computer computer = director.createComputer("i5-8500","三星520固态","微星迫击炮360M", "金士顿16G" ); System.out.println(JSON.toJSONString(computer)); } }
输出
but,but上面说过,抽象建造者接口,和导演角色可以去掉,怎么做呢?我在业务中上面的建造者模式基本不用,主要是用下面的构建方式。
自己做自己的建造者
public class ComputerBuildSelf { private final String motherBoard; // 主板 private final String cpu; // cpu private final String disk; // 硬盘 private final String powerSupplier; // 电源 private final String graphicsCard; // 显卡 private final String mouse; // 鼠标 private final String computerCase; //机箱 private final String mousePad; //鼠标垫 private final String other; //其它配件 private ComputerBuildSelf(Builder builder) { this.motherBoard = builder.motherBoard; this.cpu = builder.cpu; this.disk = builder.disk; this.powerSupplier = builder.powerSupplier; this.graphicsCard = builder.graphicsCard; this.mouse = builder.mouse; this.computerCase = builder.computerCase; this.mousePad = builder.mousePad; this.other = builder.other; } // 只暴漏getter方法到外部 public String getMotherBoard() { return motherBoard; } public String getCpu() { return cpu; } public String getDisk() { return disk; } public String getPowerSupplier() { return powerSupplier; } public String getGraphicsCard() { return graphicsCard; } public String getMouse() { return mouse; } public String getComputerCase() { return computerCase; } public String getMousePad() { return mousePad; } public String getOther() { return other; } public static Builder basic() { Builder builder = new Builder(); return builder.cpu("i5-7200u").disk("250固态硬盘").motherBoard("微星迫击炮"); } public static class Builder { private String motherBoard; // 主板 private String cpu; // cpu private String disk; // 硬盘 private String powerSupplier; // 电源 private String graphicsCard; // 显卡 private String mouse; // 鼠标 private String computerCase; //机箱 private String mousePad; //鼠标垫 private String other; //其它配件 public Builder motherBoard(String motherBoard) { this.motherBoard = motherBoard; return this; } public Builder cpu(String cpu) { this.cpu = cpu; return this; } public Builder disk(String disk) { this.disk = disk; return this; } public Builder powerSupplier(String powerSupplier) { this.powerSupplier = powerSupplier; return this; } public Builder mouse(String mouse) { this.mouse = mouse; return this; } public Builder computerCase(String computerCase) { this.computerCase = computerCase; return this; } public Builder mousePad(String mousePad) { this.mousePad = mousePad; return this; } public Builder other(String other) { this.other = other; return this; } public ComputerBuildSelf build() { return new ComputerBuildSelf(this); } } }
调用代码
public class Test { public static void main(String[] args) { // AssemblerBuilder assemblerBuilder = new AssemblerBuilder(); // Director director = new Director(assemblerBuilder); // Computer computer = director.createComputer("i5-8500","三星520固态","微星迫击炮360M", "金士顿16G" ); ComputerBuildSelf computer = ComputerBuildSelf.basic().computerCase("机箱").mouse("额外的鼠标").mousePad("额外的鼠标垫").build(); System.out.println(JSON.toJSONString(computer)); } }
ComputerBuildSelf.build()返回ComputerBuildSelf实例对象,在ComputerBuildSelf中有一个静态的内部构建类,这个Builder实质上就是具体的建造者,产品是 -->ComputerBuildSelf,抽象接口和导演类不存在。通过流式调用builder()获得这个Computer实例对象。
这部分代码:
public static Builder basic() { Builder builder = new Builder(); return builder.cpu("i5-7200u").disk("250固态硬盘").motherBoard("微星迫击炮"); }
把CPU,硬盘,主板,固定了这几个型号,意味着这个电脑对象一定会有主盘CPU,内存这3个配置,而且一定是这几种型号。当然也3个配置也是可以换的,你可以这样做
public static Builder basic() { return new Builder(); }
或者是一定有的属性做一个构造,比如我这里规定电脑要有CPU、主板和硬盘
public static Builder basic(String cpu,String motherBoard, String disk) { Builder builder = new Builder(); return builder.cpu(cpu).disk(disk).motherBoard(motherBoard); }
这样你就能随意插入自己想要的配置了。
什么时候适合自己插入配置,什么时候适合固定配置呢?这个主要看自己的使用场景了,比如调用第三方接口,支付宝、微信的请求,像APP_ID,回调地址、证书这些都是不会变的,可以使用第一种形式进行固定。第三种实际上也可以在第一种的调用方通过下面的形式对属性进行覆盖
ComputerBuildSelf computer = ComputerBuildSelf.basic().cpu("i7-9900k").disk("500G机械硬盘").computerCase("机箱").mouse("额外的鼠标").mousePad("额外的鼠标垫").build();
与工厂模式对比
工厂模式创建的对象都是一个样子,但是建造者更适合创建复杂对象,每个部分不同,构成的产品也不同。
工厂模式是只需要把这个对象给创建出来就可以了,但是建造者不仅仅创建这个对象,更要定义这个对象中的组成部分。
两种模式关注点不同,创建对象的颗粒度也不同。