建造者模式详解

建造者模式详解

为什么使用建造者模式(建造者模式的使用场景)

如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和易用性,我们可以通过构造函数配合set()方法来解决。但是,如果存在下面情况中的任意一种,我们就要考虑使用建造者模式了。

  • 我们把类的必填属性放到构造函数中,强制创建对象的时候就设置。如果必填的属性有很多,把这些必填属性都放到构造函数中设置,那构造函数就又会出现参数列表很长的问题。如果我们把必填属性通过set()方法设置,那校验这些必填属性是否已经填写的逻辑就无处安放了。
  • 如果类的属性之间有一定的依赖关系或者约束条件,我们继续使用构造函数配合set()方法
    的设计思路,那这些依赖关系或约束条件的校验逻辑就无处安放了。
  • 使用建造者模式创建对象,还能避免对象存在无效状态。比如我们定义了一个长方形类,如果不使用建造者模式,采用先创建后set的方式,那就会导致在第一个set之后,对象处于无效状态。具体代码如下所示:

Rectangle r = new Rectange(); // r is invalid
r.setWidth(2); // r is invalid
r.setHeight(3); // r is valid

如果我们并不是很关心对象是否有短暂的无效状态,也不是太在意对象是否是可变的。比如,对象只是用来映射数据库读出来的数据,那我们直接暴露set()方法来设置类的成员变量值是完全没问题的。而且,使用建造者模式来构建对象,代码实际上是有点重复的,原本的类中的成员变量,要在Builder类中重新再定义一遍。

建造者模式和工厂模式的区别

工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。

练习题

在下面的ConstructorArg类中,当isRef为true的时候,arg表示String类型的refBeanId,type不需要设置;当isRef为false的时候,arg、type都需要设置。请根据这个需求,完善 ConstructorArg类。
public class ConstructorArg {
private boolean isRef;
private Class type;
private Object arg;
// TODO: 待完善…
}

public class ConstructorArg {
    private boolean isRef;
    private Class type;
    private Object arg;

    private ConstructorArg(Builder builder) {
        this.isRef = builder.isRef;
        this.type = builder.type;
        this.arg = builder.arg;
    }

    public boolean isRef() {
        return isRef;
    }

    public Class getType() {
        return type;
    }

    public Object getArg() {
        return arg;
    }

    // use case
    ConstructorArg constructorArg = new ConstructorArg.Builder()
            .setRef(true)
            .setArg("hello")
            .setType(Student.class)
            .build();

    public static class Builder{
        private boolean isRef;
        private Class type;
        private Object arg;

        public ConstructorArg build(){
            if(isRef){
                if(arg instanceof String){
                    type = String.class;
                }else{
                    throw new IllegalArgumentException("1");
                }
            }else{
                if(arg == null || type == null){
                    throw new IllegalArgumentException("2");
                }
            }
            return new ConstructorArg(this);
        }

        public Builder setRef(boolean ref) {
            isRef = ref;
            return this;
        }

        public Builder setType(Class type) {
            this.type = type;
            return this;
        }

        public Builder setArg(Object arg) {
            this.arg = arg;
            return this;
        }
    }
}

上一篇:Spring 5.3.x源码构建


下一篇:Android 插桩入门,2021Android春招面试真题