定义

Separate the construction of a complex object from its representation so that the sameconstruction process can create different representations.

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

实现

产品类

由多个部件组成

public class Product {

    private String partA;
    private String partB;

    public String getPartA() {
        return partA;
    }

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public String getPartB() {
        return partB;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    @Override
    public String toString() {
        return "partA:" + partA + ", partB:" + partB;
    }
}

抽象建造者

规范产品的组建,一般是由子类实现。其中,buildPart方法是零件的配置,设置一个不同零件,或者不同的装配顺序就可能产生不同的产品。

public abstract class Builder {

    /**
     * 设置产品的不同组件/零件,以获得不同的产品
     */
    public abstract void buildPartA();

    /**
     * 设置产品的不同组件/零件,以获得不同的产品
     */
    public abstract void buildPartB();

    /**
     * 建造产品
     * @return
     */
    public abstract Product getProduct();

}

具体建造者

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

public class ConcreteBuilder1 extends Builder {

    private Product product = new Product();

    @Override
    public void buildPartA() {
        System.out.println("ConcreteBuilder1->buildPartA()");
        product.setPartA("组件A1");
    }

    @Override
    public void buildPartB() {
        System.out.println("ConcreteBuilder1->buildPartB()");
        product.setPartB("组件B1");
    }

    @Override
    public Product getProduct() {
        System.out.println("ConcreteBuilder1->getProduct()");
        return product;
    }
}
public class ConcreteBuilder2 extends Builder {

    private Product product = new Product();

    @Override
    public void buildPartA() {
        System.out.println("ConcreteBuilder2->buildPartA()");
        product.setPartA("组件A2");
    }

    @Override
    public void buildPartB() {
        System.out.println("ConcreteBuilder2->buildPartB()");
        product.setPartB("组件B2");
    }

    @Override
    public Product getProduct() {
        System.out.println("ConcreteBuilder2->getProduct()");
        return product;
    }
}

导演类

也叫指挥者类,负责安排已有模块的顺序,然后告诉Builder开始建造。导演类起到封装的作用,避免高层模块深入到建造者内部的实现类。在建造者模式比较庞大时,导演类可以有多个。

public class Director {

    private Builder builder;

    public Director(Builder builder){
        this.builder = builder;
    }

    /**
     * 将部件partA、partB...最后组成复杂对象(使用多个简单的对象一步一步构建成一个复杂的对象)
     */
    public void construct(){
        builder.buildPartA();
        builder.buildPartB();
    }
}

客户端代码

public class Client {

    public static void main(String[] args) {
        Builder builder1 = new ConcreteBuilder1();
        Director director1 = new Director(builder1);
        director1.construct();
        Product product1 = builder1.getProduct();
        System.out.println(product1);

        Builder builder2 = new ConcreteBuilder2();
        Director director2 = new Director(builder2);
        director2.construct();
        Product product2 = builder2.getProduct();
        System.out.println(product2);
    }
}

优点

  • 封装性

    客户端不必知道产品内部组成的细节

  • 建造者独立,容易扩展
  • 便于控制细节风险

    由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响

使用场景

  • 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。
  • 在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。

    该种场景只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段竟然没有发觉,而要通过创建者模式柔化创建过程,本身已经违反设计的最初目标。

扩展

除上面的通用源码实现外,还有另外一种更为常见的场景,就是当一个类构造器需要传入很多参数时,并且通常有一部分参数是可选的,如果使用构造方法创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用 builder模式:

public class Person {
    private final String cardId;
    private final String name;
    private final Integer age;
    private final String sex;
    private final String address;

    public static class Builder {
        // 必须的参数
        private final String cardId;
        private final String name;
        // 可选的参数
        private Integer age;
        private String sex;
        private String address;

        public Builder(String cardId, String name) {
            this.cardId = cardId;
            this.name = name;
        }

        public Builder age(Integer age) {
            this.age = age;
            return this;
        }

        public Builder sex(String sex) {
            this.sex = sex;
            return this;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }

    private Person(Builder builder) {
        this.cardId = builder.cardId;
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.address = builder.address;
    }

    @Override
    public String toString() {
        return "cardId:" + cardId + ",name:" + name + ",age:" + age + ",sex:" + sex + ",address:" + address;
    }
}
public class Client {

    public static void main(String[] args) {
        Person zhangsan = new Person.Builder("100000000000000000", "张三")
                .age(28)
                .sex("MALE")
                .address("BJ")
                .build();
        System.out.println(zhangsan);

        Person lisi = new Person.Builder("100000000000000001", "李四")
                .address("TJ")
                .build();
        System.out.println(lisi);
    }
}

上面的代码通过Person的内部类Builder创建一个Person实例,其中,李四对年龄和性别进行了”保密“(age和sex均为可选参数)。可以看到,代码看起来更为优雅,大大增强了可读性,并且客户端代码也更容易编写。

源码地址:https://gitee.com/tianranll/j...

参考文献:《设计模式之禅》


tianranll
68 声望2 粉丝