Builder模式拯救了我的强迫症

1,006 阅读5分钟

Builder

前言

Builder模式大家应该不陌生,在我们的编码生涯中,总会碰到它的身影。无论是Android开发中的AlertDialog,还是网络框架中的OkHttp和Retrofit,亦或是JavaPoet中,都有这哥们的身影。

之所以它这么受欢迎,除了它的上手难度比较低以外,还有一点就是它的的确确的解决了我们日常开发中的一个难题,创建对象时需要的参数过多

举个小例子

过去几年大家都流行炒币,导致市面上一卡难求。随着政府政策的出台,以及虚拟货币的崩盘。显卡不再是有价无市的一种状态。大学刚毕业的小龙开了个电脑店,专门给人配电脑。最开始的时候需求比较简单,只给人记录电脑的CPU,GPU,硬盘等相关信息。

传统的创建对象方式

// 电脑类
class Computer {
    private String mBroad;
    private String mCPU;
    private String mGPU;

    public Computer(String broad, String CPU, String GPU) {
        mBroad = broad;
        mCPU = CPU;
        mGPU = GPU;
    }

    @Override
    public String toString() {
        return "Computer{" +
                ", mBroad='" + mBroad + ''' +
                ", mCPU='" + mCPU + ''' +
                ", mGPU='" + mGPU + ''' +
                '}';
    }
}

这个时候创建一个Computer对象是这样的:

Computer computer = new Computer("微星 B550M","INTEL I5","NV 3060TI");

随着业务量的增大,客户的要求也越来越多。对鼠标,键盘,系统也有了相应的需求。所以Computer类也不得不有了相应的改变。

static class Computer {
    private String mOS;
    private String mBroad;
    private String mKeyBoard;
    private String mMouse;
    private String mCPU;
    private String mGPU;

    public Computer(String OS, String broad, String keyBoard, String mouse, String CPU, String GPU) {
        mOS = OS;
        mBroad = broad;
        mKeyBoard = keyBoard;
        mMouse = mouse;
        mCPU = CPU;
        mGPU = GPU;
    }

    // 就写一个set方法否则文章太长,其他就不写了
    public void setmBroad(String mBroad) {
        this.mBroad = mBroad;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "mOS='" + mOS + ''' +
                ", mBroad='" + mBroad + ''' +
                ", mKeyBoard='" + mKeyBoard + ''' +
                ", mMouse='" + mMouse + ''' +
                ", mCPU='" + mCPU + ''' +
                ", mGPU='" + mGPU + ''' +
                '}';
    }
}

而创建Computer对象的参数也越来越长:

Computer computer = new Computer("MAC OS","微星 B550M","IQUNIX F97"
,"罗技 MX MASTER3","INTEL I5","NV 3060TI");

如果再有新的需求参数,电源,机箱,散热,内存条,硬盘......简直不敢想象。

对象初始化参数问题

此时我们面对的是编程中常见的一个问题,对象中需求的参数过多,而都在构造函数传递,则构造函数就会同例子中一样,太长,要是用set方法来传递,则更为恐怖。

这个时候一个模式就应运而生,他就是建造者模式

建造者模式处理方式

/**
 * @author:TianLong
 * @date:2022/10/17 19:58
 * @detail:产品类
 */
class Computer{
    private String mOS;
    private String mBroad;
    private String mKeyBoard;
    private String mMouse;
    private String mCPU;
    private String mGPU;
    private Computer(String OS, String broad, String keyBoard, String mouse, String CPU, String GPU) {
        mOS = OS;
        mBroad = broad;
        mKeyBoard = keyBoard;
        mMouse = mouse;
        mCPU = CPU;
        mGPU = GPU;
    }

    public static ComputerBuilder createBuilder(){
        return new ComputerBuilder();
    }

    @Override
    public String toString() {
        return "Computer{" +
                "mOS='" + mOS + ''' +
                ", mBroad='" + mBroad + ''' +
                ", mKeyBoard='" + mKeyBoard + ''' +
                ", mMouse='" + mMouse + ''' +
                ", mCPU='" + mCPU + ''' +
                ", mGPU='" + mGPU + ''' +
                '}';
    }

    /**
     * @author:TianLong
     * @date:2022/10/17 19:58
     * @detail:产品建造者类
     */
    public static class ComputerBuilder{
        private String mOS = "Windows";
        private String mBroad= "微星 B550M";
        private String mKeyBoard= "无";
        private String mMouse= "无";
        private String mCPU= "Intel I5";
        private String mGPU= "AMD 6600XT";

        public ComputerBuilder setOS(String OS) {
            mOS = OS;
            return this;
        }

        public ComputerBuilder setBroad(String broad) {
            mBroad = broad;
            return this;
        }

        public ComputerBuilder setKeyBoard(String keyBoard) {
            mKeyBoard = keyBoard;
            return this;
        }

        public ComputerBuilder setMouse(String mouse) {
            mMouse = mouse;
            return this;
        }

        public ComputerBuilder setCPU(String CPU) {
            mCPU = CPU;
            return this;
        }

        public ComputerBuilder setGPU(String GPU) {
            mGPU = GPU;
            return this;
        }

        public Computer build(){
            // 可以在build方法中做一些校验等其他工作
            if (mBroad.contains("技嘉")){
                throw new RuntimeException("技嘉辱华,不支持技嘉主板");
            }
 
            Computer computer = new Computer(mOS,mBroad,mKeyBoard,mMouse,mCPU,mGPU);
            return computer;
        }
    }

老版本和Builder版本创建对象

// 老版本的Computer对象创建
Computer computer = new Computer("MAC OS","微星 B550M","IQUNIX F97"
,"罗技 MX MASTER3","INTEL I5","NV 3060TI");

// Builder版本的Computer对象创建
Computer computer =Computer.createBuilder()
        .setCPU("AMD 5600X")
        .setGPU("NV 3060TI")
        .setMouse("罗技 MX MASTER3")
        .setKeyBoard("IQUNIX F97")
        .build();

两个版本一对比就能体现出来优势。老版本构造函数中的参数太多太长,同一个类型的参数很容易传错位,经常传参数的时候,还要看看第几个参数应该传什么。

Builder模式的对象创建,简单明了,更容易理解,而且流式的调用更加美观,不会出错。

从代码中可以看到,Computer类的构造函数是私有的,保证了所有对象的创建都必须从ComputerBuilder这个类来创建。且ComputerBuilder这个类的build方法中,可以进行校验或者其他操作。

同时,Computer这个类中是否存在Set方法,由你的实际应用场景决定,反正我的使用场景里,没有修改需求。

注意事项

  1. 上述代码为常见写法,并非固定模板。只要能通过Builder类创建目标对象,都可以算是建造者模式
  2. 建造者模式中的目标对象的构造函数必须是private修饰。否则可以直接创建对象。Builder类就没有意义了
  3. 建造者模式中的目标对象是否需要Set方法,由具体需求决定。一般情况下没有Set方法,可以避免对该对象中的参数进行修改。
  4. Builder中的build方法,可以处理一些逻辑问题,比如校验信息等
  5. 工厂模式注重的是同一类型的对象中通过参数来控制具体创建哪个对象。Builder模式关注的是单一对象中的参数传递