设计模式 - 构建者 Builder Pattern

一、概念

当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。假设要创建一个 Computer 对象,cpu、ram是必选,display、keyboard是可选。

二、Java写法

2.1 构造函数重载(不推荐)

阅读起来不方便,参数太多容易混淆。

class Computer {
    private String cpu;     //必选
    private String ram;     //必选
    private String display;     //可选
    private String keyboard;    //可选
    
    public Computer(String cpu, String ram) {
        this(cpu, ram, "LG显示器");
    }
    public Computer(String cpu, String ram, String display) {
        this(cpu, ram, display, "罗技鼠标");
    }
    public Computer(String cpu, String ram, String display, String keyboard) {
        this.cpu = cpu;
        this.ram = ram;
        this.display = display;
        this.keyboard = keyboard;
    }
}

2.2  JavaBean

构建过程中对象状态容易发生变化,分步设置属性容易出错。

class Computer {
    private String cpu;     //必选
    private String ram;     //必选
    private String display = "LG";     //可选
    private String keyboard = "罗技";    //可选

    public Computer(String cpu, String ram) {
        this.cpu = cpu;
        this.ram = ram;
    }
    //setter和getter
    public String getCpu() {return cpu;}
    public void setCpu(String cpu) {this.cpu = cpu;}
    public String getRam() {return ram;}
    public void setRam(String ram) {this.ram = ram;}
    public String getDisplay() {return display;}
    public void setDisplay(String display) {this.display = display;}
    public String getKeyboard() {return keyboard;}
    public void setKeyboard(String keyboard) {this.keyboard = keyboard;}
}

2.3 构建者模式

class Computer {
    private final String cpu;     //必选
    private final String ram;     //必选
    private final String display;     //可选
    private final String keyboard;    //可选

    //目标类中创建private的构造函数,形参类型为Builder。
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.display = builder.display;
        this.keyboard = builder.keyboard;
    }

    //目标类中创建静态内部类Builder,将参数都复制到Builder中。
    public static class Builder {
        private String cpu;
        private String ram;
        private String display;
        private String keyboard;

        //Builder中创建public的构造函数,参数为目标类中必填的参数(此处为cpu、ram)。
        public Builder(String cpu, String ram) {
            this.cpu = cpu;
            this.ram = ram;
        }

        //Builder中创建设置函数,对可选参数进行赋值(此处为display、keyboard),返回Builder实例。
        public Builder setDisplay(String display) {
            this.display = display;
            return this;
        }
        public Builder setKeyboard(String keyboard) {
            this.keyboard = keyboard;
            return this;
        }
        public String getDisplay() { return display; }
        public String getKeyboard() { return keyboard; }

        //Builder中创建build()方法,返回目标类的实例。
        public Computer build() {
            return new Computer(this);
        }
    }
}
//使用
Computer computer = new Computer.Builder("因特尔", "三星")
        .setDisplay("LG")
        .setKeyboard("罗技")
        .build();

三、Kotlin写法

由于 Kotlin 支持默认参数(形参有默认值)和命名参数(指定形参名赋值),大部分情况下并不需要构建者模式,除了当我们希望一个对象经过N步才能完成构建且对象不可变时。假设 Computer 需要配置 cpu、ram 才是一个完整的对象,但它俩不能同时到位,如果先创建对象再后期 setter 进去,Computer就是可变对象了。

3.1 写法一

//目标类添加peivate主构造,属性只读供外部使用。
class Computer private constructor(
    val cpu: String,
    val ram: String,
    val display: String,
    val keyboard: String,
) {
    //目标类中添加private次构造,形参为Builder用来给目标类的属性赋值。
    private constructor(builder: Builder) : this(
        builder.cpu,
        builder.ram,
        builder.display,
        builder.keyboard
    )
    //目标类中创建内部类Builder(kt的内部类是static的)。
    class Builder(val cpu: String, val ram: String) {
        //私有化setter提供自定义的方法对属性赋值(为了通过点调用让IDE提示可以配置哪些)。
        var display: String = "LG"
            private set
        var keyboard: String = "罗技"
            private set
        fun setDisplay(str: String) = apply {display = str}
        fun setKeyboard(str: String) = apply {keyboard = str}
        //Builder中创建build()方法,返回目标类实例。
        fun build() = Computer(this)
    }
}
//使用
val computer = Computer.Builder("因特尔","三星")
    .setDisplay("LG")
    .setKeyboard("罗技")
    .build()

3.2 其他写法(不推荐)

因为不方便用小数点来让IDE显示可以配置哪些参数,还会和普通函数混在一起显示。

class Computer private constructor(
    val cpu: String,
    val ram: String,
    val display: String,
    val keyboard: String,
) {
    private constructor(builder: Builder) : this(
        builder.cpu,
        builder.ram,
        builder.display,
        builder.keyboard
    )
    //伴生对象离来完成对Builder的配置
    companion object {
        inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build()
    }
    class Builder(val cpu: String, val ram: String) {
        var display: String = "LG"
        var keyboard: String = "罗技"
        fun build() = Computer(this)
    }
}
val computer = Computer.builder {
    display = "LG"
    keyboard = "罗技"
}

猜你喜欢

转载自blog.csdn.net/HugMua/article/details/130664114