目录
场景需求
1、建造者模式中包含以下4个类
2、实际代码示例
2.1、具体产品(Product)类
2.2、抽象建造者(Builder )
2.3、具体建造者(ConcreteBuilder )
2.4、指挥者(Director)
3、简化的建造者模式
4、拥有方法链的匿名建造者
4.1、这种情况下,使用我们的建造者模式避免这种情况是个不错的实践。
4.2、JDK源码中典型的应用场景
5、建造者模式的优缺点
5.1、优点
5.2、缺点
场景需求
墨菲定律中提道:任何事都没有表面看起来的那么简单。无论在现实生活中还是在代码世界中,都存在一些复杂的对象,他们由多个部分组成,每个部分各具功能,协同运作。比如手机包含摄像头、CPU、电池等各种零部件。对于大部分用户而言,无须知道部件之间的组装细节,也几乎不会单独使用某个零部件,而是使用一部完整的手机。如果需要详细关注一个产品部件的生产、安装步骤,可以选择建造者模式对其进行设计与描述,将部件和其组装过程分开,分步创建一个复杂的对象。由于组装部件的过程复杂,因此,装配过程被分离到一个称作建造者的对象里,建造者返回给上层一个完整产品,而无需关心该产品的构建细节,这就是建造者模式的核心思想。
建造者模式(Builder Pattern)也叫做生成器模式。
将复杂对象的构建与表示分离,同构建过程,创建不同表示。隐藏复杂对象创建过程,并把这个过程加以抽象(通过子类继承或者重载的方式,动态的创建具有复合属性的对象)。
1、建造者模式中包含以下4个类
1)Product (产品类):需要为其构建对象的类,是具有不同表现形式的复杂或复合对象。
2)Builder (抽象建造者类):用于声明构建产品类的组成部分的抽象类或接口。它的作用是仅公开构建产品类的功能,隐藏产品类的其他功能;将产品类与构建产品类的更高级的类分离开。
3)ConcreteBuilder(具体建造者类):用于实现抽象建造者类接口中声明的方法。除此之外,它还通过 getResult 方法返回构建好的产品类。
4)Director(导演类):用于指导如何构建对象的类。在建造者模式的某些变体中,导演类已被移除,其角色被客户端或抽象建造者类所代替。
建造者模式通用类图 如下 图1-1:
图 1-1
2、实际代码示例
这里我就从4个角色的具体代码示例,直观的去看看建造者模式的实现方式。
2.1、具体产品(Product)类
Product 产品类,一般是多个部件组成的复杂对象,由具体建造者来创建其各个零部件。
通用代码如下:
代码语言:javascript复制package com.zhaoyanfei.designpattern.buliderpattern;
/**
* 具体产品类(可以是一个复杂对象)
* @author zhaoYanFei
*
*/
public class Product {
public void doSomething() {
System.out.println("doSomething is done.");
}
}
2.2、抽象建造者(Builder )
Builder 抽象建造者,包含创建产品各个子部件的抽象方法以及返回复杂产品的方法。
通用代码如下:
代码语言:javascript复制package com.zhaoyanfei.designpattern.buliderpattern;
/**
* 抽象建造者
* @author zhaoYanFei
*
*/
public abstract class Builder {
/**
* 设置不同的模块组装产品
*/
public abstract void setPart();
/**
* 构建产品
* @return
*/
public abstract Product builderProduct();
}
2.3、具体建造者(ConcreteBuilder )
ConcreteBuilder 具体建造者,实现抽象 Builder 定义的所有方法,并且返回一个装配好的对象。
通用代码如下:
代码语言:javascript复制package com.zhaoyanfei.designpattern.buliderpattern;
/**
* 具体建造者
* @author zhaoYanFei
*
*/
public class ConcreteBuilder extends Builder {
private Product product = new Product();
@Override
public void setPart() {
// TODO 完成产品的组装逻辑,包括组装顺序,不同方法的组合等等
}
@Override
public Product builderProduct() {
// TODO Auto-generated method stub
//返回创建好的实体类
return product;
}
}
2.4、指挥者(Director)
Director 指挥者,负责安排已有模块的顺序,然后调用 Builder 建造产品。
通用代码如下:
代码语言:javascript复制package com.zhaoyanfei.designpattern.buliderpattern;
/**
* 导演类,也叫指挥者,负责调度产品构建的方法
* @author zhaoYanFei
*
*/
public class Director {
/**
* 不同的产品由不同的方法实现
* @return
*/
public Product getProduct(){
ConcreteBuilder concreteBuilder = new ConcreteBuilder();
concreteBuilder.setPart();
return concreteBuilder.builderProduct();
}
}
3、简化的建造者模式
在建造者模式的某些实现方式中可以移除导演类。比如导演类的封装逻辑很简单,这种情况下,可以不需要导演类。简化的类图构建者模式如下图3-1:
图 3-1
这里可以看到我们只是将导演类中实现的代码移动到了客户端,但是当抽象基类和产品类太过复杂,或者需要使用建造者类从数据流中构建对象时,我们不建议这样简化修改。
4、拥有方法链的匿名建造者
构建来自相同类,但是具有不同表现形式的对象的最直接方法就是利用 Java 的多态,构建多个不同入参的构造函数,按照不同的场景进行不同的实例化操作。
4.1、这种情况下,使用我们的建造者模式避免这种情况是个不错的实践。
在 《Effective Java》一书中,Joshua Bloch 建议使用内部建造者类和方法链来代替多个构造函数。
方法链是指通过特定方法返回当前对象(this)的一种技术。通过这种技术,可以以链的形式调用方法。
注:方法链的一个限制是,只能用在不需要返回其他值的方法上,因为你需要返回 self 对象。
代码语言:javascript复制@Override
public Builder setPart() {
// TODO 完成产品的组装逻辑,包括组装顺序,不同方法的组合等等
product.setAge(20);
return this;
}
4.2、JDK源码中典型的应用场景
StringBuilder 继承了 AbstractStringBuilder 类
而 AbstractStringBuilder 实现了 Appendable 接口
可以看到 Appendable 中声明了三个追加的方法。
代码语言:javascript复制package java.lang;
import java.io.IOException;
/**
* @since 1.5
*/
public interface Appendable {
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(char c) throws IOException;
}
StringBuilder 中实现的 append 方法:
代码语言:javascript复制public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
5、建造者模式的优缺点
5.1、优点
1、封装性,在建造者模式中,调用方不必知道产品内部组成的细节,将一个复杂对象的构建与它的表示分离,使得相同的创建过程可以创建不同的产品对象。
2、扩展性,每个具体建造者都相互独立,替换具体建造者或新增具体建造者都很便捷。
3、更关注"由零件一步一步地组装出产品对象" 。将复杂产品的创建步骤拆分到不同的方法中,使得创建过程更加清晰。
5.2、缺点
1、建造者模式所创建的产品对象一般组成部分相似,如果产品的内部变化复杂,需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
2、如果产品内部结构发生变化,建造者也要相应修改,有较大的维护成本。
结尾:其实很多模式,我们在应用过程中,并不是单独的应用某种设计模式,有时候会涉及到多种模式复合使用。设计模式其实是我们的一种编程思维,是我们处理问题的方式而已。
个人建议,想系统学习某一个知识点儿,可以给自己制定一个目标,比如 23 种设计模式,我就每天学习一种设计模式,多了不学。如果你能坚持下来,其实这个过程本身就已经很厉害了。
下一节,我将带领大家了解原型模式。