定义
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
将复杂对象的构建行为拆分到一个独立对象的操作中,这样可以用相同的构建过程创建不同类型的产品。
实列
生活中,有很多类似于电脑、房子、汽车这样的的对象,它们是由很多相互关联的部件组合而成的复合体即产品。
比如说,汽车它由发动机、地盘、车身、电气等四大部分组合,而这些部件又由更小的部件构成。
对于这类复杂的产品,其构建的过程异常的复杂,不仅要了解各个部件与整体的关系,还要了解部件之间的组合关系。
因此,对于客户端来说,在没有特殊需求的情况下是不会选择自己构建的,而是由"别人"构建好,自己不关心产品是如何构建起来的,只关心如何使用。
故事
前不久,我去了一家培训机构学习设计模式,入学没几天老师便给我们布置了一个实战任务:让我们模拟现实用户使用Computer对象玩游戏。
他要求Computer类要由显示器、键盘、主板、鼠标等四个部件构成,其中键盘和鼠标是可选的,还要有一个playGame操作可以让用户玩游戏,
而且,还说要让客户端便捷地使用Computer对象,而且不同的用户对Computer的配置是有差异的。
我想这也挺简单的,创建好电脑以及各个部件后,我就新建了一个Client来模拟用户使用computer对象。
伪代码如下:
public class Client {
public static void main(String[] args){
//步骤一:构建显示器
Displayer displayer = new Displayer();
//步骤二:构建主板
MainUnit mainUnit = new MainUnit();
//步骤三:构建键盘
Keyboard keyboard = new Keyboard();
//步骤四:组装电脑
Computer computer = new Computer(mainUnit,displayer,keyboard,null);
computer.playGame();
}
}
问题
故事中,"我"显然没有领会老师的意图,Computer是一个由很多部件构成的整体即产品,而且这些部件有的是可选的。
如果,用户不了解部件的配置,还能使用电脑玩游戏吗?显然,这个场景下我们不应该要求用户熟悉各个部件。
即使,用户熟知如何配置部件,如果存在多个用户使用电脑,构建过程也难免会重复,而且可能出现的null占位符也显得不优雅。
所以,有没有一种方式即可以让客户端不关心产品如何构建的,又能复用构建过程以及消除null占位符?这便是构建者模式。
方案
构建者模式将产品的构建过程拆分成独立的步骤并封装到一个被称之为构建器(Builder)的独立对象中,
构建器支持客户端通过链式调用来构建产品,这样客户端就不再需要关心具体的部件是什么。
构建器一般要求产品对外提供配置各个部件的setter方法,而不是通过构造函数配置部件。
还有,在构建者模式有一个指导者(Director),如果我们的产品存在多个同类对象,那么我们就可以将链式调用过程封装到这个类中,这样构建同类产品时,就可以复用构建过程。
实现
接下来,我们用构建者模式实现一下故事中的程序。
首先,Computer需要对外提供setter操作来配置部件。
@Data
public class Computer {
protected Displayer displayer;
protected Keyboard keyboard;
protected MainUnit mainUnit;
protected Mouse mouse;
}
然后,创建一个具体的构建器,它实现自抽象的构建器并持有一个指向产品对象的引用。
/**抽象构建器*/
public interface IBuilder {
public IBuilder buildMainUnit();
public IBuilder buildDisplayer();
public IBuilder buildMouse();
public IBuilder buildKeyboard();
public Computer build();
}
/**具体构建器*/
public class Builder implements IBuilder {
private Computer computer;
public Builder(){
this.computer = new Computer();
}
@Override
public IBuilder buildMainUnit() {
computer.setMainUnit(new MainUnit());
return this;
}
@Override
public IBuilder buildDisplayer() {
computer.setDisplayer(new Displayer());
return this;
}
//可选部件通过set方法设置
public IBuilder buildMouse() {
computer.setMouse(new Mouse());
return this;
}
public IBuilder buildKeyboard() {
computer.setKeyboard(new Keyboard());
return this;
}
public Computer build() {
if(computer.getMainUnit()==null){
buildMainUnit();
}
if(computer.getDisplayer()==null){
buildDisplayer();
}
return computer;
}
}
现在,我们看看客户端如何构建产品。
public class Client {
public static void main(String[] args) {
IBuilder builder = new Builder();
Computer computer = builder.buildMouse().buildKeyboard().build();
computer.startGame();
}
}
最后,如果我们要复用构建过程创建不同的产品,那么可用使用指导者将构建过程封装起来。
/**指导者*/
public class Director {
private final IBuilder builder;
public Director(IBuilder builder){
this.builder = builder;
}
public Computer construct(){
return builder.buildDisplayer().
buildMainUnit().
buildKeyboard().
buildMouse().
build();
}
}
public class Client {
public static void main(String[] args) {
Computer computer = new Director( new Builder()).construct();
computer.playGame();
}
}
结构
avatar产品(Product):它是由多个部件构成的复杂对象,通常会对外提供setter操作用来设置部件。
抽象建造者(Builder):它是对构建过程的抽象,声明了产品的构建步骤。
具体建造者(Concrete Builder):它负责实现抽象类声明的步骤,步骤中封装了具体部件的构建行为,同时与一个具体的产品关联。
指挥者(Director):它负责指挥构建者构建产品,并封装了如何构建产品的逻辑,使构建同类产品时可以复用该逻辑。
总结
当一个对象由多个部件构成时,为了避免客户端和具体的部件耦合以及降低客户端使用产品的复杂度,那么我们应该考虑使用构建者模式。
它封装了部件的构建行为从而为客户端屏蔽具体的构建细节,以及提供链式操作从而降低了构建产品的复杂度。
如果期望多个产品复用相同的构建过程,那么可以使用Director封装构建过程。
网友评论