设计模式之建造者模式
一、概述
1、简介:
- 允许用户在不知道内部构建细节的情况下,可以更精细的控制对象的构造流程
- 将部件和组装过程分离,使得构建过程和部件都可以*扩展,两者之间的耦合也降到最低
2、 定义:
- 将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示
3、 使用场景:
- 相同的方法,不同的执行顺序,产生不同的事件结果时
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用
- 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时
4、优缺点
-
优点:
- 良好的封装性,使用者可以不必知道产品内部组成的细节
- 建造者独立,易于解耦
- 容易扩展
-
缺点:
- 会产生多余的Builder对象,消耗内存
5、UML类图
-
(1)Product:产品的抽象类
- 具体的产品类实现
- (2)Builder:抽象的Builder类。规范产品的组建,一般是由子类实现具体的组装过程
- (3)ConcreteBuilder:具体的builder类
- (4)Director:统一组装过程(具体的构造)
二、实现
1、 经典实现
-
背景:小成希望去电脑城买一台组装的台式主机
-
过程:
- 电脑城老板(Diretor)和小成(Client)进行需求沟通(买来打游戏?学习?看片?)
- 了解需求后,电脑城老板将小成需要的主机划分为各个部件(Builder)的建造请求(CPU、主板blabla)
- 指挥装机人员(ConcreteBuilder)去构建组件;
- 将组件组装起来成小成需要的电脑(Product)
-
(1)Builder(定义组装过程)
public abstract class Builder { //第一步:装CPU //声明为抽象方法,具体由子类实现 public abstract void BuildCPU(); //第二步:装主板 //声明为抽象方法,具体由子类实现 public abstract void BuildMainboard(); //第三步:装硬盘 //声明为抽象方法,具体由子类实现 public abstract void BuildHD(); //返回产品的方法:获得组装好的电脑 public abstract Computer GetComputer(); }
-
(2)Director(老板委派任务给装机人员)
public class Director{ //指挥装机人员组装电脑 public void Construct(Builder builder){ builder.BuildCPU(); builder.BuildMainboard(); builder.BuildHD(); } }
-
(3)ConcreteBuilder(创建具体的建造者)
//装机人员 public class ConcreteBuilder extend Builder{ //创建产品实例 Computer computer = new Computer(); //组装产品 @Override public void BuildCPU(){ computer.Add("组装CPU") } @Override public void BuildMainboard(){ computer.Add("组装主板") } @Override public void BuildHD(){ computer.Add("组装主板") } //返回组装成功的电脑 @Override public Computer GetComputer(){ return computer } }
-
(4)product(定义具体的产品类)
public class Computer{ //电脑组件的集合 private List<String> parts = new ArrayList<String>(); //用于将组件组装到电脑里 public void Add(String part){ parts.add(part); } public void Show(){ for (int i = 0;i<parts.size();i++){ System.out.println(“组件”+parts.get(i)+“装好了”); } System.out.println(“电脑组装完成,请验收”); } }
-
Test
public class Builder Pattern{ public static void main(String[] args){ //逛了很久终于发现一家合适的电脑店 //找到该店的老板和装机人员 Director director = new Director(); Builder builder = new ConcreteBuilder(); //沟通需求后,老板叫装机人员去装电脑 director.Construct(builder); //装完后,组装人员搬来组装好的电脑 Computer computer = builder.GetComputer(); //组装人员展示电脑给小成看 computer.Show(); } }
2、链式调用实现
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;
}
//UserBuilder静态内部类,每次返回的都是this
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对象。
-
User类的属性都是不可变的。所有的属性都添加了final修饰符,并且在构造方法中设置了值。并且,对外只提供getters方法。
-
由于Builder是非线程安全的,所以如果要在Builder内部类中检查一个参数的合法性,必需要在对象创建完成之后再检查。
// 线程安全 public User build() { User user = new user(this); //先实例化好对象 if (user.getAge() > 120) { throw new IllegalStateException(“Age out of range”); } return user; }
//线程不安全 public User build() { if (age > 120) { throw new IllegalStateException(“Age out of range”); } return new User(this); //最后才实例化对象 }