定义
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/java-design-patterns.git
参考文献:《设计模式之禅》