建造者模式
简介
1、定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
2、主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
3、如何使用:用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
4、解决的问题:
(1)方便用户创建复杂的对象(不需要知道实现过程)
(2)代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)
5、注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序,一般用来创建更为复杂的对象
代码实现
例子
比如麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。
![](/qrcode.jpg)
抽象建造者
abstract class Builder {
abstract Builder buildA(String msg);//汉堡
abstract Builder buildB(String msg);//饮料
abstract Builder buildC(String msg);//薯条
abstract Builder buildD(String msg); //甜品
abstract Product build();//获取套餐
}
产品
public class Product {
private String buildA="汉堡";
private String buildB="饮料";
private String buildC="薯条";
private String buildD="甜品";
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
具体建造者
public class ConcreteBuilder extends Builder{
private Product product;
public ConcreteBuilder() {
product = new Product();
}
@Override
Builder buildA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
Builder buildB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
Builder buildC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
Builder buildD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product build() {
return product;
}
}
客户类
public class Test {
public static void main(String[] args) {
ConcreteBuilder concreteBuilder = new ConcreteBuilder();
Product build = concreteBuilder.
buildA("炸鸡").buildB("可乐").buildD("甜甜卷").
build();
System.out.println(build.toString());
}
}
总结
优点
1、产品的建造和表示分离,实现了解耦。
2、将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
3、增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。
缺点
1、产品必须有共同点,限制了使用范围。
2、如内部变化复杂,会有很多的建造类,难以维护。
应用场景
建造者模式主要适用于以下应用场景:
- 相同的方法,不同的执行顺序,产生不同的结果。
- 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
- 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
- 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。
Lombok中的@Builder
简述:Builder 使用创建者模式又叫建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。
@Builder注释为你的类生成相对略微复杂的构建器API。
@Builder可以让你以下面显示的那样调用你的代码,来初始化你的实例对象:
属性介绍
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
// 如果@Builder注解在类上,可以使用 @Builder.Default指定初始化表达式
@Target(FIELD)
@Retention(SOURCE)
public @interface Default {}
// 指定实体类中创建 Builder 的方法的名称,默认为: builder (个人觉得没必要修改)
String builderMethodName() default "builder";
// 指定 Builder 中用来构件实体类的方法的名称,默认为:build (个人觉得没必要修改)
String buildMethodName() default "build";
// 指定创建的建造者类的名称,默认为:实体类名+Builder
String builderClassName() default "";
// 使用toBuilder可以实现以一个实例为基础继续创建一个对象。(也就是重用原来对象的值)
boolean toBuilder() default false;
@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface ObtainVia {
// 告诉lombok使用表达式获取值
String field() default "";
// 告诉lombok使用表达式获取值
String method() default "";
boolean isStatic() default false;
}
}
测试
@Builder(
builderMethodName = "builder", buildMethodName = "build", builderClassName = "",
toBuilder = true, access = AccessLevel.PUBLIC, setterPrefix = ""
)
public class 刘亦菲 {
@Builder.Default
private String name = "刘亦菲";
private String sex;
private final Integer age = 18;// final 字段加不加 Default 都可以初始化成功
public static void main(String[] args) {
刘亦菲 yifei = 刘亦菲.builder().build();// 如果没有加 Default,那么输出的 name 结果是 null。
/**
* 类似于拷贝,修改了 name 值,age 值还是原来的
* 如果 toBuilder = false,则没有 toBuilder 方法
*/
yifei = yifei.toBuilder().name("刘亦菲").build();
}
}
@Builder 会生成一个全参构造方法,因此就没有了无参构造方法,但当我们遇到需要无参构造方法时就会发生问题,这个时候手写或者加上 @NoArgsConstructor 都会报错,
两种解决方案
@Builder
@AllArgsConstructor
public class User {
public User() {
/**
* 手写或者 @NoArgsConstructor 都会使 @Builder 生成的全参构造函数失效
* 这个时候就被报错,加上 @AllArgsConstructor 或者手写一个全参构造函数即可
*/
}
}
使用 @Tolerate 注解
@Builder 内部做了什么
创建一个名为 ThisClassBuilder 的内部静态类,并具有和实体类相同的属性(称为构建器)
在构建器中:对于目标类中的所有的属性和未初始化的 final 字段,都会在构建器中创建对应属性
在构建器中:创建一个无参的 default 构造函数
在构建器中:实体类中的每个参数,都会对应创建类似于 setter 的方法,方法名与该参数名相同。 并且返回值是构建器本身(便于链式调用)
在构建器中:会创建一个 build 方法,调用 build 方法,就会根据设置的值进行创建实体对象
在构建器中:会生成一个 toString 方法
在实体类中:会创建一个 builder 方法,它的目的是用来创建构建器
@Builder
public class User {
private String username;
private String password;
}
public class User {
private String username;
private String password;
User(String username, String password) {
this.username = username;
this.password = password;
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
public static class UserBuilder {
private String username;
private String password;
UserBuilder() {}
public User.UserBuilder username(String username) {
this.username = username;
return this;
}
public User.UserBuilder password(String password) {
this.password = password;
return this;
}
public User build() {
return new User(this.username, this.password);
}
public String toString() {
return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ")";
}
}
}