一、前言

什么是建造者模式?或许我们对这个模式一点都不了解的。但链式调用你总写过吧,那就是建造者模式。

我举个栗子:

这是一个okhttp3使用的栗子:

        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .addHeader("Cookie", cookieString)
                .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko")
                .build();

让我们深入Request类的内部,仅保留了url字段,简化后的Request如下:

public final class Request {
    private final HttpUrl url;

    private Request(Builder builder) {
        this.url = builder.url;
    }

    public HttpUrl url() {
        return this.url;
    }


    public static class Builder {
        private HttpUrl url;

        public Builder url(HttpUrl url) {
            if (url == null) {
                throw new NullPointerException("url == null");
            } else {
                this.url = url;
                return this;
            }
        }

        public Request build() {
            if (this.url == null) {
                throw new IllegalStateException("url == null");
            } else {
                return new Request(this);
            }
        }
    }
}

我们可以发现:

  1. Request类中有一个静态内部类Builder,Builder类中的属性和Request类一一对应。
  2. Request类的构造方法接收的是一个Builder对象。
  3. Builder类中每一个设置属性的方法返回值都是Builder对象本身。
  4. Builder类中的build()方法调用Request类的构造方法,将Builder对象本身传入。

看的出来,建造者模式一步一步地将简单的对象构造成复杂的对象,我们熟悉的链式调用,就是建造者模式。

建造者模式本来应该有多个角色:

  • Product                   产品类,我们最终需要的产品,对应这里的Request
  • AbstractBuilder       抽象的建造者,规范产品的组件
  • ConcreteBuilder      具体的建造者,实现抽象的建造者中的方法,最后返回一个产品。对应这里的Builder
  • Director                   导演,封装了建造者,用于通知建造者开始建造

但随着建造者模式的简化,其中的AbstractBuilder、Director的概念已经淡化,或者说用得不多。最终演变成只有一个产品类,以及产品类中的静态建造者类。


二、实例演示

这里我们建造一个电脑,电脑由显示器、机箱、键盘和鼠标构成。

package com.yang.testBuilder;

public class Computer {
    /**
     * 显示器
     */
    private String monitor;

    /**
     * 机箱
     */
    private String box;

    /**
     * 键盘
     */
    private String keyboard;

    /**
     * 鼠标
     */
    private String mouse;

    public Computer(Builder builder) {
        this.monitor = builder.monitor;
        this.box = builder.box;
        this.keyboard = builder.keyboard;
        this.mouse = builder.mouse;
    }

    public String getMonitor() {
        return monitor;
    }


    public String getBox() {
        return box;
    }

    public String getKeyboard() {
        return keyboard;
    }

    public String getMouse() {
        return mouse;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "monitor='" + monitor + '\'' +
                ", box='" + box + '\'' +
                ", keyboard='" + keyboard + '\'' +
                ", mouse='" + mouse + '\'' +
                '}';
    }

    static class Builder {
        /**
         * 显示器
         */
        private String monitor;

        /**
         * 机箱
         */
        private String box;

        /**
         * 键盘
         */
        private String keyboard;

        /**
         * 鼠标
         */
        private String mouse;

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

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

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

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

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

测试:

package com.yang.testBuilder;

public class TestBuilderMain {
    public static void main(String[] args) {
        Computer computer = new Computer.Builder()
                .monitor("显示器")
                .box("机箱")
                .keyboard("键盘")
                .mouse("鼠标").build();

        System.out.println(computer);

    }
}

输出:

链式调用的好处在哪里?

链式调用比(无参构造方法+一个一个set)更加节省代码,更加简便。

链式调用比普通的全参构造方法更加简明,全参构造方法需要将参数设置进对应的位置上,在idea中,我们需要按照ctrl+p才能显示当前参数的名称,这一点没有链式调用方便。