Java设计模式之-建造者模式(Builder)

关于建造者模式(或者又叫构造者模式),我在网上看了很多文章。其中不乏很多人直接把建造者模式等同于builder构造器。我想说这是两种完全不同的层次和方向,一种是从系统设计层面考虑的设计模式,用于使整个系统构件间解耦更加明显,更易于扩展;另一种则是为了让构造器能适应多个参数而出现的构造函数变体。
这里不再对网上漫天的Builder构造器进行解释,只针对建造者模式进行讨论。

建造者模式:将类的使用者与类的建造方式解耦,将类的使用者与类的建造者解耦。

当然,上面这句话也是我个人对建造者模式的理解。建造者模式就是实现了三点之间的解耦:类的使用者、类的建造者、类的建造方式
我们先来看一下建造者模式的示意类图:

Java设计模式之-建造者模式(Builder)_第1张图片
建造者模式
  • Builder, 抽象建造者,定义了建造一个类(Product)需要实现的方法;
  • ConcreteBuilder,具体建造者,实现了抽象建造者定义的方法;
  • Product,产品,其实就是建造者需要建造的东西;
  • Director,其实它才是真正的类的建造者,上面所说的Builder,其实只是定义了类的建造方式,但是真正和类的使用者交互的是Director,它作为具体类的提供者,完成了建造的步骤。

OK, 我们现在来从上面说的两个解耦进行后续的解释。


将类的使用者与类的建造方式解耦

平常我们想进行一个类的构造会怎么写?一般来说我们先new一个实例出来,然后对各个变量进行set,最后做我们想做的事情。这里大家也可以换成直接在构造函数里面传入参数,大致意思是一样的。

Foo foo = new Foo();
foo.setA(123);
foo.setB("abc");
foo.setC(new bar());
foo.doSomething();

但是这种做法,实际上将类的初始化和各项配置工作都移交给了类的使用者来做,大大增强了类的使用者和类的建造方式之间的耦合关系,类的使用者必须完全了解如何建造这个类才可以,否则便等不到一个功能完好的实例,更不要说正确地使用了。

OK,那如果说我现在有这样一个产品类,会被别人使用到。我们先假设产品类是这个样子:

public class Product {
    private String name;

    private BigDecimal price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

而我们存在多种建造这个产品的方式,但首先我们需要定义一个接口,用以确定产品的建造者需要做哪些工作:

public interface IProductBuilder {
    public void giveName();
    public void givePrice();
    public Product build();
}

而后我们有了两个建造商品的类,一个生产高质量商品,另一个生产普通商品:

public class HighQualityProductBuilder implements IProductBuilder{
    private Product product = new Product();;
    @Override
    public void giveName() {
        product.setName("A High Quality Product comes out!");
    }

    @Override
    public void givePrice() {
        product.setPrice(new BigDecimal("299.99"));
    }

    @Override
    public Product build() {
        giveName();
        givePrice();
        refine();
        return product;
    }

    private void refine(){
        System.out.println("Don't know how, but the product get refined!");
    }
}
public class NormalProductBuilder implements IProductBuilder {
    private Product product = new Product();;

    @Override
    public void giveName() {
        product.setName("Product with normal quality......");
    }

    @Override
    public void givePrice() {
        product.setPrice(new BigDecimal("1.59"));
    }

    @Override
    public Product build() {
        giveName();
        givePrice();
        return product;
    }
}

这个时候我们能够看到,在Builder类中,我已经将值赋给了product的域,并且在build()方法中实现了产品的装配和建造过程。后面会提到,这其实是建造者模式的一大问题所在。

而后,我们在main方法中模拟使用该类,这样其实就能够实现了将类的使用者与类的建造方式解耦这一目的:

    public static void main(String[] args){
        IProductBuilder builder = new HighQualityProductBuilder();
        Product product = builder.build();
    }

但是到此为止,我们做得还不够。大家可以看到,在类图中展示的Builder、ConcreteBuilder、和Product都已经出现了,但是Director还迟迟不肯露面。下面我们就来说一下Director在整个建造者模式中的作用。


将类的使用者与类的建造者解耦

为了实现这一解耦目标,我们引入了Director这个参与者。它主要做的工作,就是封装了Builder,然后直接和外界类的使用者进行交互:

public class Director {
    private IProductBuilder builder;
    public Director(IProductBuilder builder){
        this.builder = builder;
    }
    
    public Product construct(){
        return builder.build();
    }
}

而后我们在main方法中使用director来替代原来直接写builder的方式:

    public static void main(String[] args){
        Director director = new Director(new NormalProductBuilder());
        Product product = director.construct();
    }

看到这里,很多人可能会骂我根本就不懂。这不还是写死了NormalProductBuilder了么?不照样是紧耦合了么?有什么意义?其实如果大家写过一点Spring就能顿悟这里面的妙处,只要我在Director构造时使用注入,并将其实现为一个Bean,就可以很轻松地将其进行解耦。由一个Director来作为选择和管理Builder的入口,而通过注入的方式将使用者与后方隔离开来,这才是建造者模式真正强大的地方。

一大问题

另外我上面也说了,建造者模式的一大问题,就是它把类的建造过程写死了,绑死了,写成了一套流程化的代码逻辑,不易更改。

所以说,建造者模式适用于不易变更的且构建复杂的类的实例化场景,而对那些生成过程经常改变的类,则不建议使用该模式,因为得到的收益可能远比不上修改的成本。

你可能感兴趣的:(Java设计模式之-建造者模式(Builder))