Builder模式
经典Builder模式(GOF-Builder)
GOF-Builder模式是一种对象创造型设计模式,用来隐藏复合对象的创建过程。他把复合对象的创造过程加以抽象
核心类
- Director:统一组建过程
- Builder:规范产品的组建,一般由子类实现具体的组建过程
- ConcreteBuilder:具体的Builder类,具体的创建对象的类
- Product:抽象产品类
- ConcreteProduct:具体的产品类
图解
这当中有两种方法


优点
- 使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 具体的建造者类之间是相互独立的,这有利于系统的扩展。
- 具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
适用场景
- 对象的创建
- 被创建的对象是一个具有符合属性的对象
- 关注对象创建的各部分创建过程----不同的工厂(builder生成器)对产品属性有不同的创建方法
使用案例
盖房子

普通写法
/**
* 普通模式(不使用设计模式时)
* 客户直接修建房子
*/
private static void normalMode() {
House house = new House();
house.setRoof("屋顶");
house.setWall("墙");
house.setFloor("地板");
System.out.println(house.toString());
}
缺点:
- 当用户想要不同类型的房子如平房、公寓等等。 用户需要手动修改三个属性,才能把房子变成平房,或者公寓。
- 房子还需要用户直接手动搭建。
不完善建造者写法
首先创造接口
package pattern_builder;
/**
* 房子建造者(由实现类实现不同的建造者)
*
*/
public interface HouseBuilder {
void makeFloor();
void makeWall();
void makeRoof();
House getHouse();// Product
}
创建平房建造者
package pattern_builder;
/**
* 平房修建者
*/
public class BungalowBuilder implements HouseBuilder {
private House house = new House();
@Override
public void makeFloor() {
house.setFloor("平房地板");
}
@Override
public void makeWall() {
house.setWall("平房墙");
}
@Override
public void makeRoof() {
house.setRoof("平房屋顶");
}
/**
* 外界使用
* */
@Override
public House getHouse() {
return house;
}
}
盖房子
/**
* 使用builder模式案例
*/
private static void builderModeDemo() {
HouseBuilder builder = new BungalowBuilder();// 平房工程队
// 让工程队开始修建
builder.makeFloor();
builder.makeRoof();
builder.makeWall();
// 用户查看 修建的房子(产品)
House house = builder.getHouse();//其实House类应该隐藏 不能让用户直接使用(new),通过相关的builder提供实例
System.out.println(house.toString());
}
缺点
用户还需要指导工程队来修,再自己查看产品,这还是不太符合我们的要求,我们的要求是用户直接到房子,然而用户还是不能一下得到产品,还需要指导者使用户多做了工作
完善的建造者写法
创建指导者
package pattern_builder;
/**
* 设计者
* 功能:指导builder
* 没有本类时,用户让builder工作并不能一部的到产品 还需要调用工程队修
*/
public class HouseDirector {
// 提供builder对象引用(参看uml类图)
private HouseBuilder houseBuilder;
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
/**
* 建造房子
*
* 让设计师指导完成
* */
public void makeHouse(){
houseBuilder.makeWall();
houseBuilder.makeRoof();
houseBuilder.makeFloor();
}
}
盖房子
/**
* 使用builder模式
*/
private static void builderMode() {
HouseBuilder builder = new BungalowBuilder();// 平房工程队
HouseDirector houseDirector = new HouseDirector(builder);// 设计指导师指导完成
houseDirector.makeHouse();// 指导师干活
// 用户查看 修建的房子(产品)
House house = builder.getHouse();//其实House类应该隐藏 不能让用户直接使用(new),通过相关的builder提供实例
System.out.println(house.toString());
}
链式调用写法
通过上文观察,我们的建造者在建造房子时,屋顶,墙,地板的建造顺序是固定的,如果我们想要在测试类中更改这些方法的顺序是不可能的(指导者内固定了顺序),这时我们感觉指导者是不是多余?如果不使用指导者的话我们试试。
我们修改接口,和实现类使其具有返回值,供我们链式调用:
package pattern_builder;
/**
* 房子建造者(由实现类实现不同的建造者)
*
*/
public interface HouseBuilder {
HouseBuilder makeFloor();
HouseBuilder makeWall();
HouseBuilder makeRoof();
House getHouse();// Product
}
package pattern_builder;
/**
* 平房修建者
*/
public class BungalowBuilder implements HouseBuilder {
private House house = new House();
@Override
public HouseBuilder makeFloor() {
house.setFloor("平房地板");
return this;
}
@Override
public HouseBuilder makeWall() {
house.setWall("平房墙");
return this;
}
@Override
public HouseBuilder makeRoof() {
house.setRoof("平房屋顶");
return this;
}
/**
* 外界使用
* */
@Override
public House getHouse() {
return house;
}
}
Java Builder模式
使用场景
在一些类中,有的参数是必要的,有的参数是不必要的。
public class User {
private final String firstName; // 必传参数
private final String lastName; // 必传参数
private final int age; // 可选参数
private final String phone; // 可选参数
private final String address; // 可选参数
}
如果采用传统的构造函数的话,就会需要写非常多的构造函数,就会让代码非常冗余。而且使用起来非常不方便。
public User(String firstName, String lastName) {
this(firstName, lastName, 0);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, age, "");
}
public User(String firstName, String lastName, int age, String phone) {
this(firstName, lastName, age, phone, "");
}
public User(String firstName, String lastName, int age, String phone, String address) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.phone = phone;
this.address = address;
}
如果采用set方法来一个属性一个属性来设定的话,就会在低效的同时使得所有属性都是可变的了。
public class User {
private String firstName; // 必传参数
private String lastName; // 必传参数
private int age; // 可选参数
private String phone; // 可选参数
private String address; // 可选参数
public User() {
}
public String setFirstName() {
return firstName;
}
public String setLastName() {
return lastName;
}
......
}
基于上述原因,就有人提出了Builder模式来解决上述问题。
使用示例
由于这个User对象是不可变的,所以毫无疑问我们给他的所有属性都加了final修饰,当然如果没有不可变的需求也是可以不加的,然后在User类中定义一个内部类Builder,这个Builder内部类中的属性要和User中的相同,并且必须有的属性要用final修饰,防止这些属性没有被赋值,其他非必须的属性不能用final,因为如果加了final,就必须对其进行初始化,这样这些非必须的属性又变成必须的。然后内部类中定义了一个构造方法,传入必须有的属性。其他非必须的属性都通过方法设置,每个方法都返回Builder对象自身。最后定义了一个build方法,将Builder对象传入User的私有构造方法,最终返回一个对象。
public class User {
private final String firstName; // 必传参数
private final String lastName; // 必传参数
private final int age; // 可选参数
private final String phone; // 可选参数
private final String address; // 可选参数
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
public static class UserBuilder {
private final String firstName;
private final String lastName;
private int age;
private String phone;
private String address;
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public UserBuilder address(String address) {
this.address = address;
return this;
}
public User build() {
return new User(this);
}
}
}
创建一个对象
User user=new User.UserBuilder("王", "小二")
.age(20)
.phone("123456789")
.address("亚特兰蒂斯大陆")
.build();