设计模式文章
中介者模式
代理模式
抽象工厂模式详解 —— head first 设计模式
装饰者模式
适配器模式
策略模式
观察者模式
建造者模式 (Builder)
建造者模式
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
介绍
意图: 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决: 主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用: 一些基本部件不会变,而其组合经常变化的时候。
如何解决: 将变与不变分离开。
关键代码: 建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
注意事项: 与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
Builder模式UML类图通用
角色介绍:
- Product——产品类 : 产品的抽象类。
- Builder——抽象类, 规范产品的组建,一般是由子类实现具体的组件过程。
- ConcreteBuilder——具体的构建者。
- Director——指挥者,统一组装过程(可省略)。
通用类型代码如下:
(1)产品类
产品类的方法和属性都是为了添加产品自身的属性,有些产品需要有多个属性,有些产品可能只有一个属性。下面的代码只是一个例子,其他属性以此方式添加即可。
/**
* 产品类
*/
public class Product {
protected String name;
public void setName(String name) {
this.name = name;
}
}
(2)抽象建造者类
与产品的各个属性相对应,并为之提供相应的方法。这个就像静态代理模式,通过代理类 Builder 来生成被代理对象 Product。
/**
* 抽象建造者
* 如果有多个产品类就有几个具体的建造者,而且这多个产品类具有相同接口或抽象类
*/
public abstract class Builder {
// 跟产品中的具体方法相对应
public abstract void setName(String name);
// 建造产品
public abstract Product create();
}
(3)具体建造者类
每个具体的 builder 都有自己独特的属性,都是对某一种具体 Product 的代理。
public class ConcreteBuilder extends Builder {
private Product product = new Product();
// 设置具体的产品名字
@Override
public void setName(String name) {
product.setName(name);
}
@Override
public Product create() {
return product;
}
}
(4)指挥者(导演)类
通过 Director 来生成具体的 Product。但其实也可以直接采用链式的方式生成,每个 ConcreteBuilder 的方法返回 this 即可,最后在调用 create 方法,即创建成功。这样就可以省去 Director 这个类,逻辑也更加清晰。
/**
* 指挥者类
* 指挥者类起到封装的作用,避免高层模块深入到建造者内部的实现类
*/
public class Director {
// 构建不同的产品
private Builder mBuilder = null;
// 根据需要传入具体的产品
public Director(Builder builder) {
mBuilder = builder;
}
// 获取最终的产品
public Product getAProduct(String name) {
mBuilder.setName(name);
// 设置不同的零件,产生不同的产品
return mBuilder.create();
}
}
其他相关模式
在《 Effective Java 第2版 》中有提到,遇到多个构造器参数时要考虑使用构建器(Builder模式)。相比于重叠构造器(telescoping constructor)模式和 JavaBeans 模式,Builder 模式实现的对象更利于使用。
1 重叠构造器模式
在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有的可选参数。下面看看其编程实现:
/**
* 使用重叠构造器模式
*/
public class Person {
//必要参数
private final int id;
private final String name;
//可选参数
private final int age;
private final String sex;
private final String phone;
private final String address;
private final String desc;
public Person(int id, String name) {
this(id, name, 0);
}
public Person(int id, String name, int age) {
this(id, name, age, "");
}
public Person(int id, String name, int age, String sex) {
this(id, name, age, sex, "");
}
public Person(int id, String name, int age, String sex, String phone) {
this(id, name, age, sex, phone, "");
}
public Person(int id, String name, int age, String sex, String phone, String address) {
this(id, name, age, sex, phone, address, "");
}
public Person(int id, String name, int age, String sex, String phone, String address, String desc) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.phone = phone;
this.address = address;
this.desc = desc;
}
}
从上面的代码中,当你想要创建实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数:
Person person = new Persion(1, "李四", 20, "男", "18800000000", "China", "测试使用重叠构造器模式");
这个构造器调用通常需要许多你本不想设置的参数,但还是不得不为它们传递值。
一句话:重叠构造器可行,但是当有许多参数的时候,创建使用代码会很难写,并且较难以阅读。
2 JavaBeans 模式
遇到许多构造器参数的时候,还有第二种代替办法,即 JavaBeans 模式。在这种模式下,调用一个无参构造器来创建对象,然后调用 setter 方法来设置每个必要的参数,以及每个相关的可选参数:
/**
* 使用JavaBeans模式
*/
public class Person {
//必要参数
private int id;
private String name;
//可选参数
private int age;
private String sex;
private String phone;
private String address;
private String desc;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAddress(String address) {
this.address = address;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
创建各个需要的对象:
Person p1=new Person();
Person p2=new Person("张三");
Person p3=new Person("李四",18);
Person p4=new Person("王五",21,180);
Person p5=new Person("赵六",17,170,65.4);
可以想象一下这样创建的坏处,最直观的就是四个参数的构造函数的最后面的两个参数到底是什么意思,可读性不怎么好,如果不点击看源码,不知道哪个是 weight 哪个是 height。还有一个问题就是当有很多参数时,编写这个构造函数就会显得异常麻烦,这时候如果换一个角度,试试 Builder 模式,你会发现代码的可读性一下子就上去了。
通过与其他两种模式的对比,才能更加清楚地知道建造者模式 (Builder) 到底好在什么地方,为什么这样设计。
参考文献
1、建造者模式