介绍
建造者模式(builder pattern),也被称为生成器模式,是一种创建型设计模式,将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示
原理
建造者模式中有四个角色:产品、指挥者、抽象建造者、具体建造者,最终的产品是由指挥者根据具体的建造者构建产品。另外其实指挥者也不是一定需要,通常情况下可以通过客户端直接调用具体建造者,不需要通过指挥者,依据实际情况而论
建造者模式和工厂模式
工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建那种类型的对象
建造者模式用来创建一种类型的复杂对象,通过设置不同的可选参数定制化的创建不同的对象
举例:顾客走进餐馆,利用工厂模式可以制作披萨、汉堡、沙拉等食物,利用建造者模式定制奶酪披萨、西红柿披萨、起司披萨等等
以下以摩拜单车和hello单车为例介绍第一种建造者模式
实现方式一
定义产品类
package com.imysh.zmy.mca.designpattern.builder.example01.product;
import lombok.Data;
/**
* @author zhangmy
* @date 2023/2/2 9:51
* @description 产品类 -- 单车
*/
@Data
public class Bike {
/**
* 车架
*/
private String frame;
/**
* 车座
*/
private String seat;
}
定义抽象建造器
package com.imysh.zmy.mca.designpattern.builder.example01.builder;
import com.imysh.zmy.mca.designpattern.builder.example01.product.Bike;
/**
* @author zhangmy
* @date 2023/2/2 9:58
* @description 抽象类建造器
*/
public abstract class Builder {
/**
* 实例化单车类共子类或实现类使用
*/
protected Bike mBike = new Bike();
/**
* 建造车架
* @return
*/
public abstract void buildFrame();
/**
* 建造车座
* @return
*/
public abstract void buildSeat();
/**
* 建造单车
* @return
*/
public abstract Bike createBike();
}
定义具体建造器
- 摩拜单车构建器
package com.imysh.zmy.mca.designpattern.builder.example01.builder;
import com.imysh.zmy.mca.designpattern.builder.example01.product.Bike;
/**
* @author zhangmy
* @date 2023/2/2 10:01
* @description 摩拜单车构建器
*/
public class MoBikeBuilder extends Builder{
@Override
public void buildFrame() {
System.out.println("建造车架");
mBike.setFrame("铝合金车架");
}
@Override
public void buildSeat() {
System.out.println("建造车座");
mBike.setSeat("真皮车座");
}
@Override
public Bike createBike() {
return mBike;
}
}
- Hello单车构建器
package com.imysh.zmy.mca.designpattern.builder.example01.builder;
import com.imysh.zmy.mca.designpattern.builder.example01.product.Bike;
/**
* @author zhangmy
* @date 2023/2/2 10:06
* @description Hello单车构建器
*/
public class HelloBikeBuilder extends Builder{
@Override
public void buildFrame() {
System.out.println("建造车架");
mBike.setFrame("碳纤维车架");
}
@Override
public void buildSeat() {
System.out.println("建造车座");
mBike.setSeat("橡胶车座");
}
@Override
public Bike createBike() {
return mBike;
}
}
定义指挥者
package com.imysh.zmy.mca.designpattern.builder.example01.director;
import com.imysh.zmy.mca.designpattern.builder.example01.builder.Builder;
import com.imysh.zmy.mca.designpattern.builder.example01.product.Bike;
/**
* @author zhangmy
* @date 2023/2/2 10:14
* @description 指挥者(智慧构建器去构建产品)
*/
public class Director {
private Builder mBuilder;
public Director(Builder mBuilder) {
this.mBuilder = mBuilder;
}
public Bike construct() {
mBuilder.buildFrame();
mBuilder.buildSeat();
return mBuilder.createBike();
}
}
使用客户端来生产产品
package com.imysh.zmy.mca.designpattern.builder.example01;
import com.imysh.zmy.mca.designpattern.builder.example01.builder.MoBikeBuilder;
import com.imysh.zmy.mca.designpattern.builder.example01.director.Director;
import com.imysh.zmy.mca.designpattern.builder.example01.product.Bike;
/**
* @author zhangmy
* @date 2023/2/2 10:22
* @description 客户端(使用客户端来建造单车)
*/
public class Client {
public static void main(String[] args) {
// 创建指挥者 -- 摩拜建造器
Director director = new Director(new MoBikeBuilder());
// 建造单车
Bike bike = director.construct();
// 输出单车信息
System.out.println("单车信息:" + bike.getFrame() + "," + bike.getSeat());
}
}
可以看出来,我们通过不同建造器传入给指挥者,指挥者就能建造出我们需要的产品
另外建造者模式还有第二种常用实现,就是lombok中的@builder注解所做的事
实现方式二
对于参数较多的类,我们创建对象通常有两种方式:构造器、setter
- 构造器
package com.imysh.zmy.mca.designpattern.builder.example02;
/**
* @author zhangmy
* @date 2023/2/2 10:40
* @description rabbitMQ客户端对象 (方式一,使用构造方法创建)
*
* 当类的属性较多时,使用构造方法去实例化对象就需要传很多参数,可读性不强,并且有可能传错位置
*/
public class RabbitMqClient1 {
private String host;
private int port;
private int mode;
private String exchange;
private String queue;
private boolean isDurable;
int connectionTimeout;
public RabbitMqClient1(String host, int port, int mode, String exchange, String queue, boolean isDurable, int connectionTimeout) {
this.host = host;
this.port = port;
this.mode = mode;
this.exchange = exchange;
this.queue = queue;
this.isDurable = isDurable;
this.connectionTimeout = connectionTimeout;
}
}
当类的属性较多时,使用构造方法去实例化对象就需要传很多参数,可读性不强,并且有可能传错位置
- setter
package com.imysh.zmy.mca.designpattern.builder.example02;
/**
* @author zhangmy
* @date 2023/2/2 10:40
* @description rabbitMQ客户端对象 (方式二,使用set方法创建)
*
* 当类的属性较多时,使用set方法创建对象解决了可读性,但是实际上客户端创建之后属性就不能改变,不能通过set方法重复修改
* 另外当需要对某一些属性做校验,而且这个校验还依赖前置属性 时,使用set方法就对顺序要求很高了,显然这不符合应用场景
*/
public class RabbitMqClient2 {
private String host;
private int port;
private int mode;
private String exchange;
private String queue;
private boolean isDurable;
int connectionTimeout;
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public void setMode(int mode) {
this.mode = mode;
}
public void setExchange(String exchange) {
this.exchange = exchange;
}
public void setQueue(String queue) {
this.queue = queue;
}
public void setDurable(boolean durable) {
isDurable = durable;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}
类的属性较多时,使用set方法创建对象解决了可读性,但是实际上客户端创建之后属性就不能改变,不能通过set方法重复修改,另外当需要对某一些属性做校验,而且这个校验还依赖前置属性 时,使用set方法就对顺序要求很高了,显然这不符合应用场景
使用建造器模式创建复杂对象
四个步骤
- 目标类的构造方法要传入一个Builder对象,并且构造方法私有;
- Builder类位于目标类的内部,并且static修饰;
- Builder类提供内置各种set方法,注意set方法返回值是builder本身;
- Builder类提供build()方法,实现目标对象的创建;
具体实现
package com.imysh.zmy.mca.designpattern.builder.example02;
/**
* @author zhangmy
* @date 2023/2/2 10:40
* @description rabbitMQ客户端对象 (方式三,使用建造者模式)
*
* 建造者模式
* 1.目标类的构造方法要传入一个Builder对象,并且构造方法私有
* 2.Builder类位于目标类的内部,并且static修饰
* 3.Builder类提供内置各种set方法,注意set方法返回值是builder本身(为了可读性set方法名字可直接写成属性名)
* 4.Builder类提供build()方法,实现目标对象的创建
*/
public class RabbitMqClient3 {
/**
* 1.私有化构造器
*/
private RabbitMqClient3(Builder builder) {}
/**
* 额外优化,增加一个builder方法这样在外部创建时可以直接调用
*/
public static Builder builder() {
return new RabbitMqClient3.Builder();
}
/**
* 2.Builder类位于目标类的内部,并且static修饰
*/
static class Builder {
private String host;
private int port;
private int mode;
private String exchange;
private String queue;
private boolean isDurable;
int connectionTimeout;
/**
* 3.Builder类提供内置各种set方法,注意set方法返回值是builder本身(为了可读性set方法名字可直接写成属性名)
* @param host
* @return
*/
public Builder host(String host) {
this.host = host;
return this;
}
public Builder port(int port) {
this.port = port;
return this;
}
public Builder mode(int mode) {
this.mode = mode;
return this;
}
public Builder exchange(String exchange) {
this.exchange = exchange;
return this;
}
public Builder queue(String queue) {
this.queue = queue;
return this;
}
public Builder durable(boolean durable) {
isDurable = durable;
return this;
}
public Builder connectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
return this;
}
/**
* 4.Builder类提供build()方法,实现目标对象的创建
* @return
*/
public RabbitMqClient3 build() {
// 在build方法里就可以去添加校验逻辑了
// 返回新的RabbitMqClient3对象
return new RabbitMqClient3(this);
}
@Override
public String toString() {
return "Builder{" +
"host='" + host + '\'' +
", port=" + port +
", mode=" + mode +
", exchange='" + exchange + '\'' +
", queue='" + queue + '\'' +
", isDurable=" + isDurable +
", connectionTimeout=" + connectionTimeout +
'}';
}
}
}
使用就是那种链式创建了
package com.imysh.zmy.mca.designpattern.builder.example02;
/**
* @author zhangmy
* @date 2023/2/2 11:07
* @description 客户端 -- 使用建造者模式去创建对象
*/
public class Client {
public static void main(String[] args) {
RabbitMqClient3 client = RabbitMqClient3.builder()
.host("127.0.0.1")
.port(5672)
.mode(1)
.exchange("ex1")
.queue("q1")
.durable(true)
.connectionTimeout(5000)
.build();
}
}
关于第二种建造者模式的使用可以安靠lombok的@builder注解的具体生成代码
评论区