建造者模式(将产品,建造者,建造规范,指挥者分开)
场景:盖房子
直接想到的方式是将房子的属性,建造房子的步骤,以及调用这些步骤建造的过程都封装到一个类里面,具体的步骤设计为抽象方法,交给子类去实现
这样做将房子和建造房子的过程耦合在了一起,我们将建造房子的过程分离出来,就成了建造者。这样职责更加分明,耦合度更低
建造者模式的四个角色
- product就是我们需要建造的产品
- Builder是建造者的抽象层,用于定义有哪些建造的步骤,具体的实现由子类来完成。同时会调用Director来建造产品而不关心产品的建造过程
- Director负责调用Builder中的步骤完成建造过程,而不关心具体的实现
- 子类只需要实现建造的具体步骤,供Director来调用即可
Director只负责掌管产品制造的顺序,Builder负责实现产品每一步的生产过程
Director相当于盖房子的指挥者,AbstractBuilder相当于应聘要求,Builder则是实际干活的工人
这样如果建造流程发生了变化,只用修改Director就行了,如果有不同的解决方案,我们只需要创建不同的Director即可,建造不同的房子我们创建对应的Builder即可(如果像之前那样写在一起,实际上会将Builder和Director自由组合,产生大量的House类,修改时会更改大量的代码,耦合度很高)
并且创建房子的步骤可能很复杂,步骤之间可能会有一些依赖关系,这时候将步骤步骤到Director里面可以使得程序逻辑更加清晰,减少出错的概率
盖房子实现
House
@Data
public class House implements Cloneable, Serializable {
private String base;
private String wall;
private String roof;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
这里可以配合原型模式,这样houseDirector每调用一次build方法都会建造一个新的房子
AbstractHouseBuilder
负责设置建造的接口与设计规范,封装建造房子的通用逻辑
public abstract class AbstractHouseBuilder {
protected House house=new House();
abstract protected void buildBase();
abstract protected void buildWall();
abstract protected void buildRoof();
public House createHouse(){
try {
return (House) house.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
CommonHouseBuilder
建造小房子的实现类
public class CommonHouseBuilder extends AbstractHouseBuilder{
@Override
protected void buildBase() {
house.setBase("5米地基");
System.out.println("打地基");
}
@Override
protected void buildWall() {
house.setWall("3米的墙");
System.out.println("盖墙");
}
@Override
protected void buildRoof() {
house.setRoof("小房顶");
System.out.println("盖房顶");
}
}
HighHouseBuilder
盖大房子的实现类
public class HighHouseBuilder extends AbstractHouseBuilder{
@Override
protected void buildBase() {
house.setBase("10米地基");
System.out.println("打地基");
}
@Override
protected void buildWall() {
house.setWall("100米墙");
System.out.println("搭建墙");
}
@Override
protected void buildRoof() {
house.setRoof("大房顶");
System.out.println("盖房顶");
}
}
HouseDirector
盖房子的指挥者,调用建造者来创建对象并交付实例对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HouseDirector {
private AbstractHouseBuilder houseBuilder;
House buildHouse(){
houseBuilder.buildBase();
houseBuilder.buildWall();
houseBuilder.buildRoof();
return houseBuilder.createHouse();
}
}
Client(调用方)
中间可以切换建造者,每次建造出的房子也都不一样
public class Client {
public static void main(String[] args) {
HouseDirector houseDirector=new HouseDirector(new CommonHouseBuilder());
System.out.println(houseDirector.buildHouse());
System.out.println(houseDirector.buildHouse());
houseDirector.setHouseBuilder(new HighHouseBuilder());
System.out.println(houseDirector.buildHouse());
}
}
源码分析StringBuilder
可以看到链式编程的思想
这里因为建造的流程本身就是不确定的,我们可以调用很多次append方法来添加字符串,所以实际的指挥者,其实是我们的业务代码。所以如果建造的流程不确定,我们应当将建造的接口暴露给Client,让Client来调用接口来建造,每一步建造都能得到建造后的结果(return this),这样也能包含建造者模式的精髓。而如果建造的过程是确定的,并且有时候会很复杂,这时候应当将建造的过程封装到Director中
抽象工厂模式专注于创建不同种类的对象
建造者模式则专注于创建不同属性的对象
工厂模式只负责将对象的创建过程和使用过程分离开,并不关心这个对象具体是怎么创建的,因而实际上创建对象的具体过程可以交给建造者模式来完成
建造者模式适用于建造同类的产品(属性要是一样的),如果需要创建的产品之间差异很大,可能不适合用建造者模式