23种设计模式之Builder模式

Builder定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Builder使用场景:

  • 相同的方法,不同的执行顺序,产生不同的事件结果时
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适
  • 当初始化一个对象特别复杂,如参数多,且很多参数都具备默认值时
    Builder的简单应用
    大家考虑一下这种场景,假如有一个类(Computer),里面有很多属性,就像下面的代码:
//计算机的抽象类,即Product角色
public class Computer {
    private String mModel;      //型号    (必传参数)
    private int mMemory;        //内存    (必传参数)
    private int mGraphics;      //显卡    (不必传参数)
    private String mBoard;      //主机    (不必传参数)
    private String mDisplay;    //显示器  (不必传参数)
    private String mProcessor;  //处理器  (不必传参数)

}

在这个类中,有些参数是必要的,而有些参数是非必要的。就好比在买电脑的时候,只需要知道型号,内存等些重要的信息,其他的信息可以关注也可以不关注的时候,那么问题就来了,如何创建这个类的对象呢?

一种可行的方案就是实用构造方法。第一个构造方法只包含两个必需的参数,第二个构造方法中,增加一个可选参数,第三个构造方法中再增加一个可选参数,依次类推,直到构造方法中包含了所有的参数,也就是构造方法的重构:

public Computer(String mModel, int mMemory) {
        this.mModel = mModel;
        this.mMemory = mMemory;
    }

    public Computer(String mModel, int mMemory, int mGraphics) {
        this.mModel = mModel;
        this.mMemory = mMemory;
        this.mGraphics = mGraphics;
    }

    public Computer(String mModel, int mMemory, int mGraphics, String mBoard) {
        this.mModel = mModel;
        this.mMemory = mMemory;
        this.mGraphics = mGraphics;
        this.mBoard = mBoard;
    }

    public Computer(String mModel, int mMemory, int mGraphics, String mBoard, String mDisplay) {
        this.mModel = mModel;
        this.mMemory = mMemory;
        this.mGraphics = mGraphics;
        this.mBoard = mBoard;
        this.mDisplay = mDisplay;
    }

    public Computer(String mModel, int mMemory, int mGraphics, String mBoard, String mDisplay, String mProcessor) {
        this.mModel = mModel;
        this.mMemory = mMemory;
        this.mGraphics = mGraphics;
        this.mBoard = mBoard;
        this.mDisplay = mDisplay;
        this.mProcessor = mProcessor;
    }

这种做法可以实现,但是大家试想,如果参数多了怎么办?可读性不好,且代码维护成本高。而且对调用者来说也比较的麻烦,还要传入不需要参数的默认值,搞不好还会出错。

第二种解决办法就出现了,我们同样可以根据JavaBean的习惯,设置一个空参数的构造方法,然后为每一个属性设置setters和getters方法。就像下面一样:


    public Computer() {
    }

    public String getmModel() {
        return mModel;
    }

    public void setmModel(String mModel) {
        this.mModel = mModel;
    }

    public int getmMemory() {
        return mMemory;
    }

    public void setmMemory(int mMemory) {
        this.mMemory = mMemory;
    }

    public int getmGraphics() {
        return mGraphics;
    }

    public void setmGraphics(int mGraphics) {
        this.mGraphics = mGraphics;
    }

    public String getmBoard() {
        return mBoard;
    }
    ...

这种方法看起来可读性不错,而且易于维护。作为调用者,创建一个空的对象,然后只需传入我感兴趣的参数。那么缺点呢?也很明显: 对象会产生不一致的状态。当你想要传入6个参数的时候,你必需将所有的setXX方法调用完成之后才行。然而一部分的调用者看到了这个对象后,以为这个对象已经创建完毕,就直接用了,其实User对象并没有创建完成。

说了那么多,就是为了衬托Builder的好处,它处理这种事情最合适不过

//计算机的抽象类,即Product角色
public class Computer {
    private final String mModel;      //型号    (必传参数)
    private final int mMemory;        //内存    (必传参数)
    private final int mGraphics;      //显卡    (不必传参数)
    private final String mBoard;      //主机    (不必传参数)
    private final String mDisplay;    //显示器  (不必传参数)
    private final String mProcessor;  //处理器  (不必传参数)

    private Computer(ComputerBuilder builder) {
        this.mModel = builder.mModel;
        this.mMemory = builder.mMemory;
        this.mGraphics = builder.mGraphics;
        this.mBoard = builder.mBoard;
        this.mDisplay = builder.mDisplay;
        this.mProcessor = builder.mProcessor;
    }

    public String getModel() {
        return mModel;
    }

    public int getMemory() {
        return mMemory;
    }

    public int getGraphics() {
        return mGraphics;
    }

    public String getBoard() {
        return mBoard;
    }

    public String getDisplay() {
        return mDisplay;
    }

    public String getProcessor() {
        return mProcessor;
    }

    public static class ComputerBuilder {
        private final String mModel;      //型号
        private final int mMemory;        //内存
        private int mGraphics;            //显卡
        private String mBoard;            //主机
        private String mDisplay;          //显示器
        private String mProcessor;        //处理器

        public ComputerBuilder(String mModel, int mMemory) {
            this.mModel = mModel;
            this.mMemory = mMemory;
        }

        public ComputerBuilder graphics(int graphics) {
            this.mGraphics = graphics;
            return this;
        }

        public ComputerBuilder board(String board) {
            this.mBoard = board;
            return this;
        }

        public ComputerBuilder display(String display) {
            this.mDisplay = display;
            return this;
        }

        public ComputerBuilder processor(String processor) {
            this.mProcessor = processor;
            return this;
        }

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

有几个重要的地方需要说明一下:

  • Computer类的构造方法是私有的。也就是说调用者不能直接创建Computer对象
  • Computer类的属性都是不可变的。所有的属性都添加了final修饰符,并且在构造方法中设置了值。并且,对外只提供getter()方法。
  • Builder模式使用了链式调用。可读性更佳。
  • Builder的内部类构造方法中只接收必传的参数,并且该必传的参数适用了final修饰符

写了那么久了,现在怎么构建一个对象?so easy

new Computer.ComputerBuilder("ThinkPad x1",8)
                .graphics(4)
                .board("华硕")
                .display("高分屏")
                .processor("i7-4600u")
                .build();

传说中的链式调用有没有,是不是相当的整洁,一行代码搞定。
builder的总结
优点:
a.良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节
b.建造者独立,容易扩展
缺点
a.会产生多余的Builder对象以及Director对象,消耗内存。

Builder模式的自动化生成
上面的builder虽然非常棒,但是需要写的东西重复性很高,这怎么能是我们该做的事情呢?在Android Studio中,可以通过安装名为InnerBuilder的插件来简化Builder模式的创建过程,在Plugins面板中搜索builder即可找到这个插件,如图:

这里写图片描述

下载完成后,会重启Android Studio使插件生效。这里以User类代码为例,在编写时,只需把属性名确定下来,然后单击鼠标右键,打开Generate菜单,选择Builder按钮,

这里写图片描述

点击Builder,在弹出的配置中勾选相关配置,如图:

这里写图片描述

点击OK,即可自动生成Builder相关代码,生成的是普通的,咱们要根据实际情况进行少量修改即可


public class User {
    private final String mFirstName; // 必选
    private final String mLastName;  // 必选
    private final String mGender;    // 非必选
    private final int mAge;         // 非必选
    private final String mPhoneNo;  // 非必选

    private User(Builder builder) {
        mFirstName = builder.mFirstName;
        mLastName = builder.mLastName;
        mGender = builder.mGender;
        mAge = builder.mAge;
        mPhoneNo = builder.mPhoneNo;
    }


    public static final class Builder {
        private String mFirstName;
        private String mLastName;
        private String mGender;
        private int mAge;
        private String mPhoneNo;

        public Builder() {
        }

        public Builder mFirstName(String val) {
            mFirstName = val;
            return this;
        }

        public Builder mLastName(String val) {
            mLastName = val;
            return this;
        }

        public Builder mGender(String val) {
            mGender = val;
            return this;
        }

        public Builder mAge(int val) {
            mAge = val;
            return this;
        }

        public Builder mPhoneNo(String val) {
            mPhoneNo = val;
            return this;
        }

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

到这里已经将Builder模式说的差不多了,有什么问题可以直接留言给我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值