reupe

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 创建一个对象,有时候是很复杂的一件事,比如在模板方法模式中讲到的Car对象,创建它,就需要装配底盘、轮胎、发动机、车身等部件,甚至之后还需要喷漆等工序。模版方法模式,主要解决的是算法中不变部分与可变部分的解耦,将可变部分算法推迟到子类中去实现;而本文要介绍的建造者模式,则是一种创建模式,顾名思义,就是为了创建对象的设计模式。


1.建造者模式

 建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的对象。

所谓对象的构建与它的表示分离,就是创建一个对象的实例和成员变量的赋值相分离,因此,不同的建造者子类根据不同的赋值就可以创建出不同的对象。

实际上一个复杂对象就是一些“部件”的聚合体,建造者模式中,通常需要一个“指挥者”Director来组织管理对每一部分的构建,以组合成一个完整的对象,再将这个对象返回给客户。

建造者模式的UML类图如下所示:

Builder : 建造者抽象父类或者接口,定义创建对象各个“部件”的接口;

ConcreteBuilder: 建造者的具体实现类,可以有不同实现,不同实现返回不同对象;

Product: 建造者对象返回的最终产品(即由各个“部件”组合而成的完整对象);

Director: 指挥者,充当客户的接口,调用建造者的接口进行“部件”的建造和“组装”,将完整对象返回给客户。符合迪米特原则。

 


2.代码实现

 汽车类,chassis底盘轴距, engine发动机功率, body车身颜色

/**
 * 汽车类,装配对象
 */
class Car {
    private static int count = 0;
    private final int id;

    public Car() {
        id = count++;
    }
    /** 汽车底盘:轴距 */
    private int chassis;
    /** 发动机: 功率*/
    private double engine;
    /** 车身: 颜色 */
    private String body;

    /** 安装底盘*/
    public void setChassis(int wheelBase){
        chassis = wheelBase;
    }
    /** 安装发动机*/
    public void setEngine(double power){
        engine = power;
    }
    /** 安装车身 */
    public void setBody(String color) {
        body = color;
    }

    public int getChassis() { return this.chassis;}
    public double getEngine() { return this.engine;}
    public String getBody() {return this.body;}

    public String toString() {
        return "car" + id + "( chassis:" + chassis + ", engine:" + engine +
                ", body:" + body +")" ;
    }
}

 

汽车建造者抽象类,结合模版方法模式, build方法实际就是模版方法。而chassis,engine,body则由具体子类实现。

/** 抽象建造者类 */
abstract class CarBuilder {
    /** 建造对象的"模板" */
    protected Car carTemp;
    /** Builder对象*/
    public CarBuilder() {
        carTemp = new Car();
    }

    /**
     * 将建造者中的汽车模板中的属性取出来赋值给实际的Car对象
     * 实际上,setChassis等是不需要的,因为这里没有参数
     * 如果有参数,就需要这些方法
     */
    public Car build() {
        Car car = new Car();
        car.setChassis(carTemp.getChassis());
        car.setEngine(carTemp.getEngine());
        car.setBody(carTemp.getBody());
        return car;
    }

    /** 装配底盘 */
    public abstract CarBuilder buildChassis();
    /** 装配发动机 */
    public abstract CarBuilder buildEngine();
    /** 装配车身 */
    public abstract CarBuilder buildBody();
}

 

低配车建造者

/**
 * 建造低配汽车的建造者
 */
class LowCarBuilder extends CarBuilder {

    @Override
    public CarBuilder buildChassis() {
        carTemp.setChassis(4200);
        return this;    //这样写可以实现对象方法的"链式调用"
    }

    @Override
    public CarBuilder buildEngine() {
        carTemp.setEngine(150.0);
        return this;
    }

    @Override
    public CarBuilder buildBody() {
        carTemp.setBody("Block");
        return this;
    }

}

 

高配车建造者

/**
 * 建造高配汽车的建造者
 */
class HighCarBuilder extends CarBuilder {

    @Override
    public CarBuilder buildChassis() {
        carTemp.setChassis(4250);
        return this;    //这样写可以实现对象方法的"链式调用"
    }

    @Override
    public CarBuilder buildEngine() {
        carTemp.setEngine(180.0);
        return this;
    }

    @Override
    public CarBuilder buildBody() {
        carTemp.setBody("White");
        return this;
    }

}

 

指挥者,作为客户调用建造者的实际接口,持有CarBuilder的引用,这里使用简单工厂模式,根据客户所传type来决定实例化哪一个Builder

/**
 * 指挥者
 */
class Director {
    private CarBuilder builder;
    private static HashMap<String, Class<? extends CarBuilder>> map = new HashMap<>();
    static {
        // 实际项目中可以扫描某包下的相关对象注册class
        map.put("low", LowCarBuilder.class);
        map.put("high", HighCarBuilder.class);
    }

    private Car invoke() {
        return builder.buildBody()
                    .buildChassis()
                    .buildEngine()
                    .build();
    }

    public Car build(String type) throws IllegalAccessException, InstantiationException {
        builder = map.get(type).newInstance();
        return invoke();
    }
}

 

客户调用

public class BuilderDemo {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Director director = new Director();
        Car car = director.build("low");
        System.out.println(car);
        car = director.build("high");
        System.out.println(car);
    }
}

 

输出结果,这里car1,car3,数字用来统计创建了多少Car实例,因为在建造者中创建了两个carTemp为car0和car2

car1( chassis:4200, engine:150.0, body:Block)
car3( chassis:4250, engine:180.0, body:White)

 


3.总结

建造者模式的意图是把构建复杂对象的逻辑分离出来,分别创建复杂对象的某个部分,然后将它们装配起来形成一个完整的复杂对象。实际开发过程中,笔者公司大量使用了lombok中的@Builder注解配合@Data,使创建DTO时自动实现了建造者模式,好处是你可以决定创建出什么样子的DTO对象而无需实现创建逻辑。还有一个地方会经常用到创建者模式,就是在做一些格式转换之类的工作时,往往创建一个Converter父类,然后根据实际业务需求,不同的子Converter类进行不同的格式转换,通常还需要一个Director或者叫Manager之类名称的类,来调用Converter,如果要实现动态地转换,在Director中还可能用到注册方式的简单工厂方法创建具体的Converter,结合创建者模式对格式进行转换。

总之,不同的设计模式要多学多用,很多时候它们不是孤立存在而是相生相伴的,这也许就是设计模式最困难,最需要大量实践经验之处吧。

 

posted on 2019-03-09 23:08  yxlaisj  阅读(277)  评论(0编辑  收藏  举报