建造者模式主要针对复杂对象的创建,是一种比较复杂的对象创建型模式。本文主要介绍:建造者模式带Director实现,无Director实现,Lombok实现、
模式背景
我们为什么要有建造者模式呢?在我们项目中可能存在一些比较复杂的对象的创建,比如一个对象里面组合了大量的其他的对象,如果将创建这些对象的代码放在使用方来编写。那么所有使用到该对象的地方都要编写巨冗长的代码,这显然不够优雅!本着公共的重复的代码就应该抽取在同一个地方的原则,我们应该将创建这个对象的职责抽取出来,交给一个专门的类来做!所以创建者模式就是为此而生的。
我们使用构建者模式可以将创建的逻辑放在一个对应的Builder类里面。比如我们现在前端需要一个返回的VO对象。这个VO对象需要根据浏览器还是移动端等情况,返回不同的结构类型,但是整体大概都是相同的一个大的VO对象。这时候我们就可以创建2个Builder:一个针对电脑的Builder,一个针对移动端的Builder。这样我们只需要编写好对应的Builder以后使用的时候就可以直接复用。
UML
原理
构建者模式,即将创建一个复杂对象的逻辑抽取到Builder类中进行,将一个复杂的对象的构建和他的表示分离。
又因为这个对象可能需要多个表示,所以我们需要对这些Builder进行抽象,使得可以扩展Builder实现类来完成不同的表示。
又因为不同Builder的实现类,只是负责复杂对象各个部分的具体实现,我们可能需要对复杂对象进行装配的时候的次序有要求:比如先装Part2再装Part1,又或者说我们某些情况不需要装配Part2了,所以这时候需要提供一个Director类来配置装配秩序。
客户端只需要和Director进行交互。
当然我个人遇到的大部分情况,都可以将Director去掉,构建的过程都放在Builder实现类中即可。
但是这个是针对构建次序要求不多的情况的(很多时候我们只需要装配好就行,并没有次序要求),如果装配次序要求的多,那么每次新的次序要求的时候,我们就要修改Builder类,这样就违背了开闭原则。
建造者模式和抽象工厂模式有点相似:都是按照一个模板来构建表示的对象。区别是建造者模式是构建一个大的复杂对象整体作为返回,抽象工厂则是返回一系列的零件对象。(返回一个车和返回车胎,车灯等的区别)
必要条件
- 需要被构建的实体类(一般是复杂的实体对象)。
- 抽象Builder
- 接口或者抽象类;
- 让各种Builder统一约束。申明
buildPartX方法以及getResult方法。
- Builder实现类。
- 为各个
Part提供具体的实现逻辑。没一个Builder实现类都应当对应着一个业务需求Bean。
- 为各个
- 一个构建器
- 目的1:让build的过程和客户端进行隔离。
- 目的2:真正的控制复杂对象的构建逻辑,Builder只是把方法都确定好了,Director负责调用来装配。
实现
实体类(我们这里以一个汽车为例子)
public class Car {
private String light;
private String wheel;
private String chair;
}
构建抽象类
public abstract class CarBuilder {
/**
我们将Car对象在这里创建,实现类继承就好了。
为什么Car对象在Builder中创建,我想是为了和Director进行隔离吧。因为从对象->Builder->Director中间有层次关系,尽量将每一层的隔离做好。
*/
Car car = new Car();
public abstract void buildLight();
public abstract void buildWheel();
public abstract void buildChair();
/**这个可以是一个静态方法*/
public Car createCar() {
return car;
}
}
具体Builder实现
public class BigCarBuilder extends CarBuilder {
@Override
public void buildLight() {
this.car.setLight("big light");
}
@Override
public void buildWheel() {
this.car.setWheel("big wheel");
}
@Override
public void buildChair() {
this.car.setChair("big chair");
}
}
指挥类
public class Director {
/** 构造者模式第二个关键点:
* 指挥类里面内持一个builder,让客户端只在乎使用哪个builder来构建就行。
* 通过这样客户端就完全只需要知道自己用哪个builder就行了,然后初始化好builder直接往指挥类里面塞。 */
public CarBuilder builder;
public Director(CarBuilder builder) {
this.builder = builder;
}
public CarBuilder getBuilder() {
return builder;
}
/**构建对象*/
public Car build() {
builder.buildLight();
builder.buildWheel();
builder.buildChair();
return builder.createCar();
}
}
使用
CarBuilder builder = new BigCarBuilder();
//具体使用哪个builder来构造对象,可以使用配置文件来配置,增加灵活度。
Director director = new Director(builder);
Car car = director.build();
System.out.println(car);
优缺点
- 优点
- 职责拆分细,扩展性相对就高些。
- 缺点
- 结构复杂,代码量大,你是不是也觉得为了创建一个对象都一下子就延伸了2个步骤很繁琐。
就是上面全部必要条件的实现。如果构建一个对象很复杂,还是推荐使用该方式进行结构设计。
无Director实现
之前说过,在某些情况下,我们可以将Director类和Builder进行融合,将Director的职责交给Builder,构建的过程让Builder自己也同时完成。
实现
abstract class CarBuilder2 {
protected Car car = new Car();
public abstract void buildLight();
public abstract void buildWheel();
public abstract void buildChair();
/*builder自己来构建*/
public Car build() {
this.buildWheel();
this.buildChair();
this.buildLight();
return car;
}
}
public class NoDirectorCarBuilder extends CarBuilder2 {
@Override
public void buildLight() {
this.car.setLight("no director light");
}
@Override
public void buildWheel() {
this.car.setWheel("no director wheel");
}
@Override
public void buildChair() {
this.car.setChair("no director chair");
}
/*验证*/
public static void main(String[] args) {
NoDirectorCarBuilder builder = new NoDirectorCarBuilder();
Car car = builder.build();
System.out.println(car);
}
}
这么做简化了 Director类,但是也将职责全交给了Builder,这也必然会加重了Builder的职责。
但是当一个对象要求十分复杂的时候,还是推荐使用Director的方式来进行构建。 扩展性更高,也更加符合单一职责原则。
Lombok实现
Java开发中经常使用的插件:lombok 中有一个@Builder注解用来很方便,他也是利用了构建者模式。使用这个注解来创建对象还是很方便的。其内部实现方式如下:
class Person {
private String name;
private String age;
/*lombok开始*/
/**
* 关键4 构建对象
*/
public static PersonBuilder bulder() {
return new PersonBuilder();
}
/**
* 关键1 构造
*/
public Person(PersonBuilder builder) {
this.name = builder.name;
this.age = builder.age;
}
/**
* 关键2 构建类
*/
public static class PersonBuilder {
/**
* 关键3 属性copy
*/
private String name;
private String age;
public PersonBuilder name(String name) {
this.name = name;
return this;
}
public PersonBuilder age(String age) {
this.age = age;
return this;
}
public Person build() {
return new Person(this);
}
}
public static void main(String[] args) {
Person p = Person.bulder().name("justin").age("24").build();
System.out.println(p.getName());
}
}
其内部会为当前类创建一个Builder类,该类拥有和原始类同样的属性,我们通过builder来对属性就行赋值。最后通过builder的对象来构造原始类的对象。
优缺点
整体来看,构建者模式的优缺点如下:
优点
- 对象的创建和表示分离,符合解耦的思想。
- 每个构建者相互独立,可以动态的添加或者替换构建者来创建不同的表示,扩展性高。
- 一定程度上,可以让我们更清晰的了解复杂对象的创建逻辑(只需要盯准Director类就行了)。
缺点
- 适用性是那种大致相同,只是各个组成部分都有自己不同的表示的。如果差异太大不适合该模式。
- 如果对复杂对象需求变化太多,会增加很多构建类,增加了系统复杂度。
使用场景
- 构建复杂的对象,一个对象内部包含了很多其他的对象。
- 复杂对象对外的表示需要能扩展,很有可能产品会对这个复杂对象的表示提出新需求。
- 生成的复杂对象,对象创建过程中,内部的对象有相互依赖的关系,使用Director可以很好的控制和让我们理解这些顺序。
附
如有代码和文章问题,还请指正!感谢!