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

使用场景:

1、 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。

2、 当构造过程必须允许被构造的对象有不同的表示时。

通用类图:

 
举例:我们生活当中有许多设备都是以组装的形式存在的,例如台式电脑,那么有些厂商就会推出一些具有默认配置的组装电脑主机(这里可以用到模板方法模式来实现),顾客可以购买默认配置的产品,也可以要求厂商 重新组装 一部不同配置不同组装方式的主机。此时,我们就可以使用建造者模式来满足特殊顾客的要求了。

注意到这个例子中厂商是 重新组装一部主机,即关注点是主机的每个组成部分,这就符合上面Builder模式给出的使用场景了。

    具体类图如下:

简单代码实现如下:

//抽象产品类,使用了模板方法模式,不同产品有不同的“组成部分part”
abstract class AbstractProduct{
   protected abstract void part01();
   protected abstract void part02();
   protected abstract void part03();
    
   //模板方法给出了默认的组装方式,生成默认的产品
   public final AbstractProduct defaultProduct() {
    part01();
    part02();
    part03();
     return this;//返回当前对象,即默认组装方式的产品
  }
}

//具体的产品A、B,不同产品实现了不同的“组成部分part”
class ConcreteProductA extends AbstractProduct{
   protected void part01() {
    System.out.println( "产品A :part01() ...");
  }
   protected void part02() {
    System.out.println( "产品A :part02() ...");
  }
   protected void part03() {
    System.out.println( "产品A :part03() ...");
  }
}

class ConcreteProductB extends AbstractProduct{
   protected void part01() {
    System.out.println( "产品B :part01() ...");
  }
   protected void part02() {
    System.out.println( "产品B :part02() ...");
  }
   protected void part03() {
    System.out.println( "产品B :part03() ...");
  }
}

//抽象建造者,制定每一种产品应该实现的组合方式buildPart()和生产buildProduct()的标准
abstract class AbstractBuilder{
   public abstract void buildPart();
   public abstract AbstractProduct buildProduct();
}


/*
* 具体建造者,如果对于默认产品(即当调用抽象产品中的defaultProduct()方法)不满意时,
* 可以不调用它来获得产品,而是使用具体的建造者来改变产品的生产组装方式,以得到不同的产品
*/

class ConcreteBuilderA extends AbstractBuilder{
   private AbstractProduct productA = new ConcreteProductA();
    
   public void buildPart() {
     this.productA.part03();
     this.productA.part02();
     this.productA.part01();
  }
    
   public AbstractProduct buildProduct() {
     return this.productA;
  }
}

class ConcreteBuilderB extends AbstractBuilder{
   private AbstractProduct productB = new ConcreteProductB();
    
   public void buildPart() {
     this.productB.part02();
     this.productB.part01();
     //特地省略掉产品B中的一个组成部分,例如该部分的功能顾客不需要
//    this.productB.part03();
  }
    
   public AbstractProduct buildProduct() {
     return this.productB;
  }
}

//导演类,预先持有各个产品的建造者,为需要不同于默认产品的用户提供不同的组装方式
class Director{
   private AbstractBuilder builderA = new ConcreteBuilderA();
   private AbstractBuilder builderB = new ConcreteBuilderB();    
    
   public AbstractProduct getProductA() {
     this.builderA.buildPart();
     return this.builderA.buildProduct();
  }
    
   public AbstractProduct getProductB() {
     this.builderB.buildPart();
     return this.builderB.buildProduct();
  }
}

//测试类
public class Client {
   public static void main(String[] args) {
    System.out.println( "利用模板方法模式获得默认的产品A");
    AbstractProduct defualtProductA = new ConcreteProductA().defaultProduct();    
    
    System.out.println( "\n利用Director类获得不同组装方式的产品A");
    Director director = new Director();
    director.getProductA();
    
    System.out.println( "\n利用Director类获得不同组装方式的产品B");
    director.getProductB();
  }
}
测试结果:

利用模板方法模式获得默认的产品A

产品A :part01() ...

产品A :part02() ...

产品A :part03() ...

利用Director类获得不同组装方式的产品A

产品A :part03() ...

产品A :part02() ...

产品A :part01() ...

利用Director类获得不同组装方式的产品B

产品B :part02() ...

产品B :part01() ...

其实在这个例子当中,产品类那一部分用到了上一篇文章讲到的模板方法模式,即defaultProduct()提供了一个产品的默认组成部分的组装方式。
 
但是这里我有个疑问, AbstractProduct 类中根据模板方法模式提供的的所谓默认组装方式只是打印出几句测试的话而已,又不是真正返回一个具体产品,但是上面例子中那样返回一个当前对象( return this; )的处理方式不知道是否合理?
 
另外,在写了这几篇关于用 Java代码 实现设计模式的文章之后,发现这个建造者 Builder模式似乎是结合了抽象工厂模式、模板方法模式。上面一段已经说过我的疑惑,至于抽象工厂模式,我个人是觉得上面代码例子中的Director类就很类似抽象工厂的具体工厂类了,但是Director类还要负责build一下产品的组装方式才返回一个产品,也许就是这个“build一下”才显得建造者模式关注于产品各个部分的组装,而抽象工厂模式仅仅只是关注于一个最终产品的生成。
 
之前看过一句话说大概是说:计算机方面的任何一个问题如果难以解决,都可以通过增加一个中间层来处理。现在想了一下,好像Abstract FactoryBuilder模式都是运用了这一“原理”来达到想要的效果。譬如Abstract Factory中有个抽象工厂类,Builder中有个Director类,说到底也就是封装隐藏某些细节,并从实现和使用这两者之间解耦出来吧。
 
我觉得,一定要先理解了各个模式的关注点和适用场景之后才能更好地把握这些吧。
 
可能这几个模式都是创建型的模式而且我没有什么实战经验才会使得我对于这些有点混淆了...不怕,在它们全部实现的过程中一点点思考,慢慢地运用到实际当中去应该就会逐渐明白的了。