设计模式详解之建造者模式

建造者模式属于创建型模式,提供了一种创建对象的最佳方式。

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

主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

建造者模式的四个对象

Product产品类:

通常是实现了模板方法模式(这里可以后续看我的更新博客,如果我更新了),也就是有模板方法和基本方法。

Builder抽象建造者:

规范产品的组建,一般是由子类实现。

ConcreteBuilder具体建造者:

实现抽象类定义的所有方法,并且返回一个组建好的对象。

Director导演类:

负责安排已有模块的顺序,然后告诉Builder开始建造。

例子

举个例子:

  • 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
  • 汽车购买者(用户):你只要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(比如车轮,车门,发动机,方向盘等等))

同样的,我们以这个例子用代码来实现一下更能清晰的了解。

我们的汽车类(Car),也就是我们的产品

package builder;

public abstract class Builder {
    private String wheel;  //车轮
    private String carDoor; //车门
    private String engine;  //发动机
    private String steeringWheel;  //方向盘

	...我们的get和set方法(为了简洁,我就不展示了)...

    @Override
    public String toString() {
        return "Builder{" +
                "wheel='" + wheel + '\'' +
                ", carDoor='" + carDoor + '\'' +
                ", engine='" + engine + '\'' +
                ", steeringWheel='" + steeringWheel + '\'' +
                '}';
    }
}

我们的建造抽象类(Builder

package builder;

public abstract class Builder {

    abstract void buildWheel(); //建造车轮
    abstract void buildCarDoor(); //建造车门
    abstract void buildEngine(); //建造发动机
    abstract void buildSteeringWheel(); //建造方向盘

    //建造完成,获得车辆
    abstract Car getCar();
}

我们的建造实现类(ConcreteBuilder

package builder;

public class ConcreteBuilder extends Builder{

    private Car car;

    //定义一个空构造方法,在实例化的同时实例我们的车
    public ConcreteBuilder(){
        car = new Car();
    }

    @Override
    void buildWheel() {
        car.setWheel("轮子");
    }

    @Override
    void buildCarDoor() {
        car.setCarDoor("车门");
    }

    @Override
    void buildEngine() {
        car.setEngine("发动机");
    }

    @Override
    void buildSteeringWheel() {
        car.setSteeringWheel("方向盘");
    }

    @Override
    Car getCar() {
        return car;
    }
}

我们的导演类(Director),用来指挥我们的创建过程,比如创建顺序

package builder;

public class Director {

    public Car carBuilder(Builder builder){
        //按照我们的导演的顺序来创建车辆
        builder.buildWheel();
        builder.buildCarDoor();
        builder.buildEngine();
        builder.buildSteeringWheel();
        
        //创建完之后,返回我们创建完的车辆
        return builder.getCar();
    }

}

我们在测试类中测试一下

package builder;

public class Test {

    public static void main(String[] args) {

        //创建一个我们的导演,指挥
        Director director = new Director();
        //导演指挥我们具体的实现类去创建车辆
        Car car = director.carBuilder(new ConcreteBuilder());
        System.out.println(car.toString());
    }

}


//测试结果
Car{wheel='轮子', carDoor='车门', engine='发动机', steeringWheel='方向盘'}
Process finished with exit code 0

可以看到,通过这样的建造者模式,我们的复杂过程被我们封装了起来。但是我们也发现了我们的导演可以指挥顺序,但是里面具体的参数,比如想建造什么无法修改。其实也可以,我们对导演类进行扩展一下就行。

我们通过静态内部类实现零件无序装配构造,这种方式使用更加灵活,更符合定义。使用的时候可以根据用户需求自定义更改内容。我们对其修改后,示例代码如下:

我们对抽象建造的抽象方法添加了传递参数,修改了返回类型

package builder;

public abstract class Builder {

    abstract Builder buildWheel(String msg); //建造车轮
    abstract Builder buildCarDoor(String msg); //建造车门
    abstract Builder buildEngine(String msg); //建造发动机
    abstract Builder buildSteeringWheel(String msg); //建造方向盘

    //建造完成,获得车辆
    abstract Car getCar();
}

然后在实现类进行实现

package builder;

public class ConcreteBuilder extends Builder{

    private Car car;

    //定义一个空构造方法,在实例化的同时实例我们的车
    public ConcreteBuilder(){
        car = new Car();
    }

    @Override
    Builder buildWheel(String msg) {
        car.setWheel(msg);
        return this;
    }

    @Override
    Builder buildCarDoor(String msg) {
        car.setCarDoor(msg);
        return this;
    }

    @Override
    Builder buildEngine(String msg) {
        car.setEngine(msg);
        return this;
    }

    @Override
    Builder buildSteeringWheel(String msg) {
        car.setSteeringWheel(msg);
        return this;
    }

    @Override
    Car getCar() {
        return car;
    }
}

这时我们在导演类里面,就可以自由的传递我们需要的参数来建造车辆,也可以指定顺序。

package builder;

public class Director {

    public Car carBuilder(Builder builder){
        //创建自定义车辆,自定义建造顺序
        return builder.buildWheel("厉害的车轮")
                .buildCarDoor("漂亮的车门")
                .buildEngine("马力十足的引擎")
                .buildSteeringWheel("舒服的方向盘")
                .getCar();
    }

}

测试类的测试结果

Car{wheel='厉害的车轮', carDoor='漂亮的车门', engine='马力十足的引擎', steeringWheel='舒服的方向盘'}

Process finished with exit code 0

总结

建造者模式关注的是零件类型和装配顺序,我们可以总结一下它的优缺点和使用场景。

优点:

  • 封装性:产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
  • 建造者独立,容易扩展:具体的建造者之间是相互独立的,这有利于系统的扩展,增加新的具体建造者无需修改原有类库的代码,符合”符合开闭原则“。
  • 便于控制细节风险:由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

缺点:

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间

差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

使用场景:

  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
  • 适合于一个具有较多的零件(属性)的产品(对象)的创建过程

补充:

通过学习建造者模式,我们可以发现这与我们的抽象工厂模式很是相像,但是也是有不同的地方。(关于工厂模式可以看我的设计模式详解之工厂模式

  • 与抽象工厂相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
  • 在抽象工厂模式中,客户端示例工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端不可以直接调用建造者的相关方法,而是通过导演类来指导如何生成对象,包括对象的组装过程和建造顺序,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
  • 如果将抽象工厂看成汽车配件生产工厂,生产一个产品族的的产品,那么建造者模式就是一个汽车组装工厂,通过对部件组装可以返回一辆完整的汽车。

建造者模式最主要的功能是基本方法的调用顺序安排,因为组装顺序不同对象效能也不同,这才是建造者模式要表
达的核心意义。

参考资料

(书籍)大话设计模式

(视频)遇见狂神说:通俗易懂的23种设计模式教学

posted @ 2020-06-03 20:59  CryFace  阅读(212)  评论(0编辑  收藏  举报