Builder建造者模式

Builder模式有什么用?
       简洁明了的使用方式、灵活多变的链式调用、复杂对象的构建、增加代码可读性


先看一个例子

package com.example.demo.builder;

public class User {

    private String name;
    private String sex;
    private int age;
    private double height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
}

如果现在我要创建出一个User对象,那么一般我们会调用User对象的构造方法进行赋值,比如这样:

public User(String name, String sex, int age, double height) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.height = height;
}

在使用时可以调用对应的构造方法来创建出一个User对象

new User("张三", "男", 20, 1.7)

那么问题来了,这个对象中有四个属性,分别是姓名、性别、年龄、身高,假设年龄、身高不是必须输入的字段,如何处理?该怎么去构建这个对象呢?
这时我们可以多写几个构造方法,涵盖所有属性就可以解决,比如:

package com.example.demo.builder;

public class User {

    private String name;
    private String sex;
    private int age;
    private double height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public User(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public User(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.height = height;
    }

    public User(String name, String sex, int age, double height) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
    }

}

多写几个构造方法不就解决了么,非常简单,对不对。
但是这样做的坏处其实已经体现的很明显了
       1、参数目前我们有4个字段,如果是二十个呢?三十个呢?就会导致写非常多的构造方法,代码会变的难以阅读,而且很难维护。
       2、当我调用构造方法的时候,我怎么知道我的第三第四个参数是代表什么意思?万一传参搞混了呢?参数如果非常多的话,我岂不是要写一个参数,在去看一眼构造方法,哦~原来下一个参数要传的是age而不是height,我这里一个是int一个是double,如果写反的话编译就过不去,但是如果两个类型都是一样的话,的确是有搞混的风险。


除了用构造方法外,还有有一种最常用的属性赋值的方式set方法

final User user = new User();
user.setName("张三");
user.setAge(20);
user.setSex("男");
user.setHeight(1.7);

但是这种方法也有缺点
       1、对象处于不一致状态,在多线程环境下,其中一个线程创建对象,另外一个线程使用第一个线程创建的对象,就会有潜在的风险,当第一个线程执行到setSex的时候挂起,第二个线程以为对象已经创建完毕,就直接拿来使用,但是在这时候其实对象还没有创建完,就会有状态不一致的问题。
       2、不够灵活,对于复杂点的对象创建起来逻辑和层次不够明显
比如,在User类中在增加一个配偶的对象

package com.example.demo.builder;

public class User {

    private String name;
    private String sex;
    private int age;
    private double height;
    // 配偶
    private Spouse spouse;

    private static class Spouse {
        private String spouseName;
        private int spouseAge;
        private double spouseHeight;

        public String getSpouseName() {
            return spouseName;
        }

        public void setSpouseName(String spouseName) {
            this.spouseName = spouseName;
        }

        public int getSpouseAge() {
            return spouseAge;
        }

        public void setSpouseAge(int spouseAge) {
            this.spouseAge = spouseAge;
        }

        public double getSpouseHeight() {
            return spouseHeight;
        }

        public void setSpouseHeight(double spouseHeight) {
            this.spouseHeight = spouseHeight;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public Spouse getSpouse() {
        return spouse;
    }

    public void setSpouse(Spouse spouse) {
        this.spouse = spouse;
    }

    public User(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public User() {}

    public User(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.height = height;
    }

    public User(String name, String sex, int age, double height) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
    }

    public static void main(String[] args) {
        final User user = new User();
        user.setName("张三");
        user.setAge(20);
        user.setSex("男");
        user.setHeight(1.7);
    }

}

这时候在用set方法的时候,会变成这样

final User user = new User();
user.setName("张三");
user.setAge(20);
user.setSex("男");
user.setHeight(1.7);

final Spouse spouse = new Spouse();
spouse.setSpouseName("李四");
spouse.setSpouseAge(18);
spouse.setSpouseHeight(1.67);
user.setSpouse(spouse);

看起来层次感和逻辑体现的并不是特别清晰
这时就可以利用Builder模式来解决上面遇到的所有问题


Builder建造者模式
      先附上代码

package com.example.demo.builder;

import lombok.Getter;
import lombok.Setter;

@Getter
public class User {

    private String name;
    private String sex;
    private int age;
    private double height;
    private boolean isHaveSpouse;
    private Spouse spouse;

    @Getter
    @Setter
    private static class Spouse {
        private String spouseName;
        private int spouseAge;
        private double spouseHeight;
        private final Builder builder;

        public Spouse(Builder builder) {
            this.builder = builder;
        }

        private Spouse(String spouseName, int spouseAge, double spouseHeight, Builder builder) {
            this.spouseName = spouseName;
            this.spouseAge = spouseAge;
            this.spouseHeight = spouseHeight;
            this.builder = builder;
        }

        public Spouse spouseName(String spouseName) {
            this.spouseName = spouseName;
            return this;
        }

        public Spouse spouseAge(int spouseAge) {
            this.spouseAge = spouseAge;
            return this;
        }

        public Spouse spouseHeight(double spouseHeight) {
            this.spouseHeight = spouseHeight;
            return this;
        }

        public Builder end() {
            this.builder.spouse = new Spouse(spouseName, spouseAge, spouseHeight, builder);
            return this.builder;
        }
    }

    public User(Builder builder) {
        this.name = builder.name;
        this.sex = builder.sex;
        this.age = builder.age;
        this.height = builder.height;
        this.isHaveSpouse = builder.isHaveSpouse;
        this.spouse = builder.spouse;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private String name;
        private String sex;
        private int age;
        private double height;
        private boolean isHaveSpouse;
        private Spouse spouse;

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder sex(String sex) {
            this.sex = sex;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder height(double height) {
            this.height = height;
            return this;
        }

        public Builder notHaveSpouse() {
            this.isHaveSpouse = false;
            return this;
        }

        public Spouse haveSpouse() {
            this.isHaveSpouse = true;
            return new Spouse(this);
        }

        public User build() {
            return new User(this);
        }

    }

}

这里为了让代码稍微少点,用了lombok的注解,整体来看用Builder的话代码量会稍微多一些,但是在使用时会更加方便、易扩展。
编写Builder模式的几个步骤:

1、在类中创建一个静态内部类Builder,Builder类中涵盖了User类的所有属性
2、在Builder类中对每个属性赋值,与set的区别在于赋值后返回自己
3、创建build方法,用来返回外部对象User
4、在User对象中,编写一个builder的静态方法,主要用于通过类名.builder的方式获取构造对象

使用时通过类名.builder().属性()的方式来使用
User类无配偶的例子:

User build = User.builder()
            .name("张三")
            .sex("男")
            .age(20)
            .height(1.7)
            .notHaveSpouse()
            .build();

User类有配偶的例子:

User build = User.builder()
            .name("张三")
            .sex("男")
            .age(20)
            .height(1.7)
            .haveSpouse()
                .spouseName("李四")
                .spouseAge(18)
                .spouseHeight(1.65)
                .end()
            .build();

如果是不设置成有配偶,是调用不了spouseName、spouseAage…这些字段的,必须调用haveSpouse才可以调用配偶相关的方法,相当于是在创建对象时增加了条件约束,在特定方法之后才可以调用相关方法。


总结
优点:利用builder方式来构建对象的好处,从上面的两行代码就会发现,链式调用,逻辑清晰、可读性好,使用时非常简单、整洁
缺点:在编写Builder构造的过程中,代码量会比较多,也会有些逻辑在里面,在写的时候会稍微有一点复杂,不像setXXX这种方式非常的无脑
不过对于客户端使用的时候来说,非常方便,同时因为是在最后调用的build,所以也不存在不一致的问题。


留下人生足迹,一步一个脚印

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值