设计模式 -- 创建者模式(Builder Pattern)

 

       我们先来看一个生活中的场景, 经过了一上午工作,饥肠辘辘的小王来到了食堂想要饕餮一番,食堂的饭菜着实不错啊,有30道菜,5种水果可供选择,一番纠结后, 小王最终选了一个大鸡腿、一份红烧肉、一碟小青菜、另配米饭和汤。  然后美滋滋的吃了起来。 

       来分析一下该场景:食堂提供了很多种菜肴和水果(此处假设这些菜品种类长期不变), 而在小王的午饭中,米饭和汤是固定的, 另外他会挑选其中几个菜做为午饭; 将这个场景抽象一下,就是构建一个复杂对象时, 这个对象一些属性是固定不变的,由于需求不确定,其它更多的属性是可选择的;

   

     在这样的场景下, 我们该怎么去用代码实现呢?

 

       方法一: 用构造函数来实现

       这个不必多说, 为了实现所有的搭配,我们需要创建大量的构造函数来实现这种需求,即使真的码出了这么多构造函数,万一途中不小心把两个参数的顺序搞混了,编译的时候是不会出错的,但程序运行时就会表现出错误的行为。所以此方法是不可取的。

 

     方法二: 用JavaBean来实现

      先来看一下代码:

public class Lunch {

    /** 米饭 */
    private int rice;
    /** 汤 */
    private int soup;
    /** 鸡腿 */
    private int drumstick;
    /** 鱼 */
    private int fish;
    /** 番茄 */
    private int tomato;
    /** 豆腐 */
    private int tofu;

    /** 此处省略其它几十样菜品 **/

    /** 是否打包 */
    private boolean takeout;

    /** 此处省略一堆 get&set 方法 **/

    public Lunch buildLunch(){
        Lunch lunch = new Lunch();
        lunch.setRice(1);
        lunch.setSoup(1);
        lunch.setDrumstick(2);
        lunch.setTofu(1);
        lunch.setTakeout(false);
        return lunch;
    }

 

        使用这种方式的好处很明显, 创建对象很容易, 而且代码方便阅读;

        但也是有缺点的, 因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态;需要开发人员花费额外的精力去维护其在多线程情况下的安全性;

   

     方法三: 用创建者模式(Builder Pattern)来实现

      顺便提一下,Java 中, 我们经常用到的 StringBuilder 底层使用的就是创建者模式。

      先来看一下代码:

public class Lunch {

    /** 
      将成员变量声明为final, 初始化后不可变,这样可保证线程安全; 
      很多业务场景中,成员变量是需要经常变化的,这样场景下不必声明为final;
    */
    /** 米饭 */
    private final int rice;
    /** 汤 */
    private final int soup;
    /** 鸡腿 */
    private int drumstick;
    /** 鱼 */
    private int fish;
    /** 番茄 */
    private int tomato;
    /** 豆腐 */
    private int tofu;
    /** 是否打包 */
    private boolean takeout;

    public Lunch(LunchBuilder lunchBuilder){
        this.rice = lunchBuilder.rice;
        this.soup = lunchBuilder.soup;
        this.fish = lunchBuilder.fish;
        this.drumstick = lunchBuilder.drumstick;
        this.tomato = lunchBuilder.tomato;
        this.tofu = lunchBuilder.tofu;
        this.takeout = lunchBuilder.takeout;
    }

    public static class LunchBuilder {
        /**
         * 固定属性
         */
        public final int rice;
        /**
         * 固定属性
         */
        public final int soup;
        /** 鸡腿 */
        public int drumstick;
        /** 鱼 */
        public int fish;
        /** 番茄 */
        public int tomato;
        /** 豆腐 */
        public int tofu;
        /** 是否打包 */
        public boolean takeout;

        public LunchBuilder(int rice, int soup) {
            this.rice = rice;
            this.soup = soup;
        }

        public LunchBuilder fish(int fish) {
            this.fish = fish;
            return this;
        }

        public LunchBuilder drumstick(int drumstick) {
            this.drumstick = drumstick;
            return this;
        }

        public LunchBuilder tomato(int tomato){
            this.tomato = tomato;
            return this;
        }

        public LunchBuilder tofu(int tofu){
            this.tofu = tofu;
            return this;
        }

        public LunchBuilder takeout(boolean takeout) {
            this.takeout = takeout;
            return this;
        }

        /**
         * 在调用build方法之前,都不会实例化对象
         */
        public Lunch build() {
            return new Lunch(this);
        }
    }

    @Override
    public String toString() {
        return "Meal{" +
                "rice='" + rice + '\'' +
                ", soup='" + soup + '\'' +
                ", fish='" + fish + '\'' +
                ", drumstick='" + drumstick + '\'' +
                ", tomato='" + tomato + '\'' +
                ", tofu='" + tofu + '\'' +
                ", takeout=" + takeout +
                '}';
    }

    public static void main(String[] args){
        // 按需构建复杂的对象
        Lunch lunch1 = new Lunch.LunchBuilder(1, 1).drumstick(2).tofu(2).takeout(Boolean.FALSE).build();

        Lunch lunch2 = new Lunch.LunchBuilder(1, 1).fish(1).tomato(1).takeout(Boolean.FALSE).build();

        System.out.println(lunch1);

        System.out.println(lunch2);
    }

}

   我们来分析一下上面的代码: 

   1.  外部类 Lunch 的构造函数, 其参数是它的静态内部类 LunchBuilder ; 静态内部类 LunchBuilder 中的成员变量, 和其外部类 Lunch 的成员变量完全一致;

   2.  当构建一顿午饭, 米饭和汤是标配, 可以将其指定为静态内部类LunchBuilder的构造函数的参数, 这样每次构建一顿午饭都少不了要配上米饭和汤;   若是想要某个属性一旦初始化后就不可更改, 可以使用final关键字来修饰;

   3.  在 LunchBuilder 中,为每个不是final的成员变量,增加一个方法, 在方法中会为这个变量赋值,然后返回当前的builder;

   4.  在 LunchBuilder 中,   有一个build() 方法, 这个方法会调用外部类Lunch的构造函数, 只要最终调用了build方法,才会真正创建一个外部类的对象出来;

 

  文中代码可在此处找到: https://github.com/tiny-v/review_java/tree/master/pattern/src/main/java/com/my/pattern/builder 

 

  如有不对的地方, 欢迎各位前来指正。

 

---- THE  END ----

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值