建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。
使用多个简单的对象一步一步构建成一个复杂的对象,有点像造房子一样一步步从地基做起到万丈高楼。我想这也是为什么被称呼为建造者模式的原因吧!反正我是找不出更好的理由了。这样理解反而更容易记住。
概念:
- 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示!
- 主要作用:在用户不知道 对象的建造过程和细节 的情况下就可以直接创建复杂的对象。
- 如何使用:用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
解决的问题:
- 方便用户创建复杂的对象(不需要知道实现过程)
- 代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)
例子:造汽车 & 买汽车。 1.工厂(建造者模式):负责制造汽车(组装过>程和细节在工厂内) 2.汽车购买者(用户):你只需要说出你需要的>型号(对象的类型和内容),然后直接购买就可>>以使用了 (不需要知道汽车是怎么组装的(车轮、车门、>发动机、方向盘等等))
指挥者(Director)直接和客户(Client)进行需求沟通;
沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求(Builder);
将各个部件的建造请求委派到具体的建造者(ConcreteBuilder);
各个具体建造者负责进行产品部件的构建;
最终构建成具体产品(Product)。
实现方式
研究了好久发现关于建造者模式的实现例子有好多,有造人、造车、造房子、造世界的...等好多。但归类后有两种实现方式。
- 通过Client、Director、Builder和Product形成的建造者模式
- 通过静态内部类方式实现零件无序装配化构造
方式一
通过Client、Director、Builder和Product形成的建造者模式!
角色分析:
- 抽象建造者(builder):描述具体建造者的公共接口,一般用来定义建造细节的方法,并不涉及具体的对象部件的创建。
- 具体建造者(ConcreteBuilder):描述具体建造者,并实现抽象建造者公共接口。
- 指挥者(Director):调用具体建造者来创建复杂对象(产品)的各个部分,并按照一定顺序(流程)来建造复杂对象。
- 产品(Product):描述一个由一系列部件组成较为复杂的对象。
既然是建造者模式,那么我们还是继续造房吧,其实我也想不到更简单的例子。假设造房简化为如下步骤:(1)地基(2)钢筋工程(3)铺电线(4)粉刷 ; “如果”要盖一座房子,首先要找一个建筑公司或工程承包商(指挥者)。承包商指挥工人(具体建造者)过来造房子(产品),最后验收。步骤抽象如下:
- 创建抽象建造者定义造房步骤
- 创建工人具体实现造房步骤
- 创建承包商指挥工人施工
- 验收,检查是否建造完成
代码编写
建造者:Builder.java
/**
* 建造者
*/
abstract class Builder {
//地基
abstract void bulidA();
//钢筋工程
abstract void bulidB();
//铺电线
abstract void bulidC();
//粉刷
abstract void bulidD();
//完工-获取产品
abstract Product getProduct();
}
产品:Product.java
/**
* 产品(房子)
*/
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
具体建造者:ConcreteBuilder.java
/**
* 具体建造者(工人)
*/
public class Worker extends Builder{
private Product product;
public Worker() {
product = new Product();
}
@Override
void bulidA() {
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void bulidB() {
product.setBuildB("钢筋工程");
System.out.println("钢筋工程");
}
@Override
void bulidC() {
product.setBuildC("铺电线");
System.out.println("铺电线");
}
@Override
void bulidD() {
product.setBuildD("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct() {
return product;
}
}
指挥者:Director.java
/**
* Director.java * 指挥者
*/
public class Director {
//指挥工人按顺序造房,指挥者可以随意控制建造的顺序!
public Product create(Builder builder) {
builder.bulidA();
builder.bulidB();
builder.bulidC();
builder.bulidD();
return builder.getProduct();
}
}
测试类:Test.java
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//包工头
Director director = new Director();
//包工头指挥具体的工人创建房子,生成具体的产品!
Product create = director.create(new ConcreteBuilder());
System.out.println(create.toString());
}
}
上面示例是 Builder模式的常规用法,导演类 Director 在 Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。
方式二
通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品
角色分析
- 抽象建造者
- 具体建造者
- 产品
比第一种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得产品的创建更加简单灵活。
比如:比如麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。步骤抽象如下:
- 创建建造者定义麦当劳的产品
- 创建服务员实现具体产品
- 服务员随意搭配套餐出售给客户
代码
建造者:Builder.java
/**
* 建造者
*/
abstract class Builder {
//汉堡
abstract Builder bulidA(String mes);
//饮料
abstract Builder bulidB(String mes);
//薯条
abstract Builder bulidC(String mes);
//甜品
abstract Builder bulidD(String mes);
//获取套餐
abstract Product build();
}
产品:Product.java
/**
* Product.java
* 产品(麦当劳套餐)
*/
public class Product {
private String buildA="汉堡";
private String buildB="饮料";
private String buildC="薯条";
private String buildD="甜品";
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
}
具体建造者:Worker.java
public class Worker extends Builder{
private Product product;
public Worker() {
product = new Product();
}
@Override
Product build() {
return product;
}
@Override
Builder bulidA(String mes) {
product.setBuildA(mes);
return this;
}
@Override
Builder bulidB(String mes) {
product.setBuildB(mes);
return this;
}
@Override
Builder bulidC(String mes) {
product.setBuildC(mes);
return this;
}
@Override
Builder bulidD(String mes) {
product.setBuildD(mes);
return this;
}
}
测试类:Test.java
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
ConcreteBuilder concreteBuilder = new ConcreteBuilder();
//可以自由组合,如果不组合,也有默认的套餐!
Product build = concreteBuilder
.bulidA("牛肉煲")
// .bulidC("全家桶")
.bulidD("冰淇淋")
.build();
System.out.println(build.toString());
}
}
总结
优点
- 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
- 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则“。具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响。
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
应用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
- 需要生成的对象内部属性本身相互依赖。
- 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
建造者与抽象工厂模式的比较
- 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族 。
- 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象 。
- 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!