设计模式之建造者模式

定义

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

结构

  • Product,被构建的复杂产品,一般包含多个部件。
  • Builder,建造者接口,定义了构建复杂对象的多个部件的方法。
  • ProductBuilder,具体建造者,实现了建造者接口。
  • Director,指挥者,负责安排复杂对象的构建顺序。

简单实现

具体产品,包含3个部件

public class Product {

  private String partA;
  private String partB;
  private String partC;

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

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

  public void setPartC(String partC) {
    this.partC = partC;
  }

  public void show() {
    System.out.println("partA: " + partA + ",partB: " + partB + ",partC: " + partC);
  }
}

建造者接口

/**
 * 建造者接口
 */
public interface Builder {

  void buildPartA(String partA);

  void buildPartB(String partB);

  void buildPartC(String partC);

  Product getResult();
}

具体建造者

/**
 * 具体建造者
 */
public class ProductBuilder implements Builder {

  private Product product = new Product();

  @Override
  public void buildPartA(String partA) {
    product.setPartA(partA);
  }

  @Override
  public void buildPartB(String partB) {
    product.setPartB(partB);
  }

  @Override
  public void buildPartC(String partC) {
    product.setPartC(partC);
  }

  @Override
  public Product getResult() {
    return product;
  }

}

指挥者

/**
 * 指挥者
 */
public class Director {

  private Builder builder;

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

  /**
   * 真正构建
   */
  public Product construct(String partA, String partB, String partC) {
    builder.buildPartA(partA);
    builder.buildPartB(partB);
    builder.buildPartC(partC);
    return builder.getResult();
  }

}

客户端

public class Client {

  public static void main(String[] args) {
    Builder builder = new ProductBuilder();
    Director director = new Director(builder);
    Product product = director.construct("AAA", "BBB", "CCC");
    product.show();
  }

}

一般情况下,我们会使用静态内部类的方法来实现建造者模式,在一个产品类内部自动带有一个具体建造者,不再需要建造者接口和指挥者,这样使建造者模式更加简洁。

public class Product {

  private String partA;
  private String partB;
  private String partC;

  private Product(ProductBuilder builder) {
    this.partA = builder.partA;
    this.partB = builder.partB;
    this.partC = builder.partC;
  }

  public static ProductBuilder builder() {
    return new ProductBuilder();
  }

  public void show() {
    System.out.println("partA: " + partA + ",partB: " + partB + ",partC: " + partC);
  }

  /**
   * 具体建造者
   */
  public static class ProductBuilder {

    private String partA;
    private String partB;
    private String partC;

    public ProductBuilder partA(String partA) {
      this.partA = partA;
      return this;
    }

    public ProductBuilder partB(String partB) {
      this.partB = partB;
      return this;
    }

    public ProductBuilder partC(String partC) {
      this.partC = partC;
      return this;
    }

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

  }
}

客户端代码如下

public class Client {

  public static void main(String[] args) {
    Product product = Product.builder()
        .partA("aaa")
        .partB("bbb")
        .partC("ccc")
        .build();
    product.show();
  }

}

建造者模式在JDK和Guava中的实现

JDK中实现

JDK中构建字符串的建造者StringBuilder

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence {
    ...
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
    ...
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
}

客户端使用

public class TestStringBuilder {

  public static void main(String[] args) {
    String result = new StringBuilder()
        .append(12)
        .append(true)
        .append("abc")
        .toString();
    System.out.println(result);
  }

}

Guava中实现

Guava中缓存的实现CacheBuilder

public final class CacheBuilder<K, V> {
    public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
    checkState(
        expireAfterWriteNanos == UNSET_INT,
        "expireAfterWrite was already set to %s ns",
        expireAfterWriteNanos);
    checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
    this.expireAfterWriteNanos = unit.toNanos(duration);
    return this;
  }
  public CacheBuilder<K, V> initialCapacity(int initialCapacity) {
    checkState(
        this.initialCapacity == UNSET_INT,
        "initial capacity was already set to %s",
        this.initialCapacity);
    checkArgument(initialCapacity >= 0);
    this.initialCapacity = initialCapacity;
    return this;
  }
  public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
    checkWeightWithWeigher();
    checkNonLoadingCache();
    return new LocalCache.LocalManualCache<>(this);
  }
}

客户端使用

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;

public class TestCacheBuilder {

  public static void main(String[] args) {
    Cache<String, String> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(5, TimeUnit.SECONDS)
        .initialCapacity(30)
        .softValues()
        .recordStats()
        .build();
    System.out.println(cache);
  }

}

Guava中不可变集合的实现

import com.google.common.collect.ImmutableList;

public class TestImmutableList {

  public static void main(String[] args) {
    ImmutableList<String> list = ImmutableList.<String>builder()
        .add("aaa")
        .add("bbb")
        .add("ccc")
        .build();
    System.out.println(list);
  }

}

总结

优点

  1. 将产品的构建过程和表现分离,耦合度低,方便扩展。
  2. 可以更加精细的控制产品的创建过程。

缺点

  1. 如果产品的内部结构复杂多变,当产品内部发生变化,建造者也要同步修改,后期维护成本较大。

本质

建造者模式的本质就是分离整体构建算法和部件构造。

使用场景

  1. 需要创建的产品对象内部结构比较复杂,需要分离构建过程和使用,这种情况下可以考虑使用建造者模式。

参考

大战设计模式【17】—— 建造者模式
设计模式(六)——建造者模式(源码StringBuilder分析)
设计模式的征途—6.建造者(Builder)模式
研磨设计模式-书籍

posted @ 2021-08-11 22:40  strongmore  阅读(38)  评论(0编辑  收藏  举报