介绍:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
场景:我们在创建对象时可能会碰到过一个问题,比如,如果需要赋值的属性过多,构造函数会很多,而且有的会很长。通过JavaBean的方式可能不需要担心构造函数,但是,我们的set部分又会很长很难看,这里推荐大家试试builder模式
一、角色及作用
角色 | 作用 |
---|---|
产品(Product) | 目标创建的复杂对象 |
抽象生成器(Builder) | 提供创建一个Product对象的各个组件的方法及返回Product对象的方法 |
具体生成器(ConcreteBuilder) | 实现Buidler接口的类。 |
管理者(Director) | 用于管理builder对象的工作 |
二、场景
玩过《我的世界》这类沙盒游戏的玩家,很多都是冲着其开放式的制作功能去的。比如,我们要建造一个房子就需要以下:墙,屋顶,门,床。但是这些东西的制作也十分麻烦。如果每次都让客户端来创建会需要很多代码
内部组件
// 床
public class Bed {
public Bed(String s) {
System.out.println(s);
}
}
// 门
public class Door {
public Door(String s) {
System.out.println(s);
}
}
// 屋顶
public class Roof {
public Roof(String s) {
System.out.println(s);
}
}
// 墙
public class Wall {
public Wall(String s) {
System.out.println(s);
}
}
1.使用构造函数的方式
public class House {
public Bed bed;
public Door door;
public Roof roof;
public Wall wall;
public House(Bed bed){
this.bed = bed;
}
public House(Bed bed,Door door){
this.bed = bed;
this.door = door;
}
// ......省略各种组合的构造函数
}
可见,这种方式会使此类中存在很多构造方法,而且不能满足所有的组合,客户端在制作房屋的时候,比较不适用。
2.使用setter方式
public class House {
public Bed bed;
public Door door;
public Roof roof;
public Wall wall;
// setter and getter
}
典型的java bean 的形式,解决上面构造函数的不足,但是让我们来看看使用。
House house = new House();
house.setBed(1);
// ......省略其它set方法
如果我们的私有属性增加到二十,那么我们每次创建的时候就需要写二十行setter代码,累不累?
总结:
上面这两种方法,在每次创建房屋的时候,客户端还需要每次去创建各种类型的对象,会产生很多重复代码。
三、使用builder模式
产品-房子
public class House {
public Bed bed;
public Door door;
public Roof roof;
public Wall wall;
}
抽象生成器
public interface Builder {
public void bed();
public void door();
public void roof();
public void wall();
public House getProduct();
}
生成器的具体实现:木屋
public class WoodHouseBuilder implements Builder {
private House house = new House();
@Override
public void bed() {
house.bed=new Bed("木床");
}
@Override
public void door() {
house.door = new Door("木门");
}
@Override
public void roof() {
house.roof = new Roof("木头屋顶");
}
@Override
public void wall() {
house.wall = new Wall("木墙");
}
@Override
public House getProduct() {
return house;
}
}
生成器的具体实现:别墅
public class VillaHouseBuilder implements Builder {
private House house = new House();
@Override
public void bed() {
house.bed = new Bed("木床");
}
@Override
public void door() {
house.door = new Door("铁门");
}
@Override
public void roof() {
house.roof = new Roof("欧式带天窗屋顶");
}
@Override
public void wall() {
house.wall = new Wall("石墙");
}
@Override
public House getProduct() {
return house;
}
}
build管理者
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.wall();
builder.roof();
builder.door();
builder.bed();
}
}
开始制作房子
public static void main(String[] args) {
System.out.println("开始做木屋=======");
Builder builder = new WoodHouseBuilder();
Director director = new Director(builder);
director.construct();
System.out.println("开始做别墅=======");
Builder builder2 = new VillaHouseBuilder();
director = new Director(builder2);
director.construct();
}
输出
开始做木屋=======
木墙
木头屋顶
木门
木床
开始做别墅=======
石墙
欧式带天窗屋顶
铁门
木床
这样,我们可以明显感觉到在创建对象时候代码量少了很多。而且,对于不同款式的房子,我们只需要不同的Director,对象的属性和创建分离。
优点:
- 生产过程交给Director,客户端不必知道产品的内部结构
- 可以有多个生成器,方便扩展
四、《Effective Java》中的Builder模式
在《Effective Java》中也有对Builder模式的使用,但是感觉在实现上和上面还是有点区别。参考:《Effective Java》 第二章,第2条:遇到多个构造器参数时要考虑用构造器
public class NutFacts {
private final int serSize;
private final int fat;
private final int car;
private final int so;
public static class Builder {
// 必填字段
private final int serSize;
// 非必填字段
private int fat = 0;
private int car = 0;
private int so = 0;
public Builder(int serSize) {
this.serSize = serSize;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder car(int val) {
car = val;
return this;
}
public Builder so(int val) {
so = val;
return this;
}
public NutFacts build() {
return new NutFacts(this);
}
}
private NutFacts(Builder builder) {
serSize = builder.serSize;
fat = builder.fat;
car = builder.car;
so = builder.so;
}
}
创建对象的方法:
NutFacts no = new NutFacts.Builder(1).car(2).so(1).build();
比较:
这个创建对象的方法就像Python中的具名的可选参数。
builder可以对参数加强约束条件,build方法可以用于检验这些约束。
与构造器相比,builder的优势在于可以有多个可变参数,builder参数可以在创建对象期间调整。
和setter相比,属性可以不可变,如上面NutFacts,属性都是不可变得final。但是通过构造器对Builer对象修改,又可以修改属性。
更多模式: 一天一个设计模式—分类与六大原则