23种设计模式之Builder模式

来源:互联网 发布:淘宝蛋糕店 编辑:程序博客网 时间:2024/06/06 01:54

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模式说的差不多了,有什么问题可以直接留言给我。

1 0