引言

之前在家看两个小外甥玩轨道车,拆开包装,一堆小零件,兄弟两一个拼桥梁、弯道、路标,一个装车、搭立交、组装上下坡。不一会儿轨道就全拼好了,两兄弟用代表自己的车子在轨道上追逐,玩的很开心。我看了下轨道车包装,根据使用零件多少不同,组拼顺序不同,摆放不同可以创建不同的轨道和街道,有椭圆形的,上下立交式的,单层的……

忽然想到用程序来表述玩轨道车的流程,如果用类图描述轨道车的玩法,可以简单表示为:

设计模式——建造者模式-LMLPHP

分为两个部分:

红色为轨道车的各个部件,Road规定了轨道车可以有 坡度、弯道、桥梁、路标、汽车、立交这几个部分;

蓝色部分为建造不同的轨道,环形轨道(AnnularBuilder)和立交轨道(InterchangeBuilder)。

Road为模板方法的变形形式,轨定轨道车的部件和各个部件的组装顺序,实现如下:

public abstract class Road {
    //坡度
    protected abstract void slope();
    //弯道
    protected abstract void curve();
    //桥梁
    protected abstract void bridge();
    //路标
    protected abstract void guide();
    //汽车
    protected abstract void car();
    //立交
    protected abstract void interchange();

    private List<String> list = new ArrayList<>();

    final public void create() throws InvocationTargetException, IllegalAccessException {
        Method[] methods = this.getClass().getDeclaredMethods();
        for (String method : list) {
            for (int i = 0; i < methods.length; i++) {
                if (methods[i].getName().equals(method)) {
                    methods[i].invoke(this, null);
                }
            }
        }
    }

    final public void setList(List<String> methods){
        this.list = methods;
    }
}

组装轨道车的部件实现方法:

public class TrackRoad extends Road {
    //坡度
    @Override
    protected void slope() {
        System.out.println("建造上下坡……");
    }

    //弯道
    @Override
    protected void curve() {
        System.out.println("建造曲线车道……");
    }

    //桥梁
    @Override
    protected void bridge() {
        System.out.println("建造桥梁……");
    }

    //路标
    @Override
    protected void guide() {
        System.out.println("放置公路路标……");
    }

    //汽车
    @Override
    protected void car() {
        System.out.println("建造汽车……");
    }

    //立交
    @Override
    protected void interchange() {
        System.out.println("建造立交……");
    }
}

抽象创建轨道车类Builder定义了轨道车的组装部件和获取组装的轨道车

public abstract class Builder {
    /**
     * 不同部件的创建
     */
    public abstract void setPart(List<String> methods);

    /**
     * 建造轨道
     */
    public abstract TrackRoad buildRoad() throws InvocationTargetException, IllegalAccessException;
}

具体轨道车玩法建造类(立交轨道车):

public class InterchangeBuilder extends Builder {
    private TrackRoad road = new TrackRoad();
    @Override
    public void setPart(List<String> methods) {
        this.road.setList(methods);
    }

    @Override
    public TrackRoad buildRoad() {
        return this.road;
    }
}

如果想创建一个立交轨道,可以这么创建:

InterchangeBuilder interchangeBuilder = new InterchangeBuilder();
List<String> interMethods = new ArrayList<>();
interMethods.add("slope");
interMethods.add("bridge");
interMethods.add("guide");
interMethods.add("interchange");
interMethods.add("car");
interchangeBuilder.setPart(interMethods);
interchangeBuilder.buildRoad().create();

引入导演类

坡度、弯道、桥梁、路标、汽车、立交不同组装方式可以构造不同的轨道,为了方便支持的很多不同的轨道,可以增加个导演类(Director)。

设计模式——建造者模式-LMLPHP

导演类封装立交轨道和环形轨道的实现,对外提供直接获取的方法:

public class Director {
    private List<String> steps = new ArrayList<>();
    private InterchangeBuilder interchangeBuilder = new InterchangeBuilder();
    private AnnularBuilder annularBuilder = new AnnularBuilder();

    public TrackRoad getInterchangeBuilder(){
        System.out.println("===========================建造立交车道===========================");
        this.steps.clear();
        this.steps.add("slope");
        this.steps.add("bridge");
        this.steps.add("guide");
        this.steps.add("interchange");
        this.steps.add("car");
        this.interchangeBuilder.setPart(this.steps);
        return this.interchangeBuilder.buildRoad();
    }

    public TrackRoad getAnnularBuilder(){
        System.out.println("===========================建造曲线车道===========================");
        this.steps.add("curve");
        this.steps.add("bridge");
        this.steps.add("guide");
        this.steps.add("car");
        this.annularBuilder.setPart(this.steps);
        return this.annularBuilder.buildRoad();
    }
}

客户端不关注如何实现,只需要拿来即用(可玩):

    public void testDerictor() throws InvocationTargetException, IllegalAccessException {
        Director director = new Director();
        director.getAnnularBuilder().create();

        director.getInterchangeBuilder().create();
    }

其实这就是建造者模式,由导演类决定如何构建具体的对象(产品)。

建造者模式

定义

  将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

通用类图

设计模式——建造者模式-LMLPHP

四要素

建造者模式中有以下4个角色:

Product产品类

通常是实现模板方法模式(有模板方法和基本方法的类),例子里的TrackRoad(轨道)就属于产品类。

Builder抽象建造者

规范产品的组建,一般是抽象类,约定功能由子类去实现具体的建造方法,对应例子里的Builder。

ConcreteBuilder具体建造者

实现抽象建造者的所有方法。例子里的InterchangeBuilder和AnnularBuilder就是具体轨道的建造者。

Director导演类

封装具体建造者,提供简单易用的构建产品类方法。

总结

优点

封装性:将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。

建造者独立,容易扩展:增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。

缺点

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

使用场景

  • 相同的方法,不同的执行顺序,产生不同的事件结果时
  • 多个部件或零件都可以装配到一个对象中,但是产生的运行结果又不相同时
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能
10-05 22:17