设计模式-Builder模式

Builder模式属于创建型模式。
它是将一个复杂对象的构建过程隐藏起来,让使用者只关系自己要生成什么样的对象。即创建和表示分离。

一个例子(做汤)

我需要做两种汤:小鸡炖蘑菇汤和海带排骨汤。
做汤工序如下:

海带排骨汤:

  1. 加入海带;
  2. 加入排骨;
  3. 加入海带排骨精粉;
  4. 煮汤;

小鸡炖蘑菇:

  1. 加入小鸡儿;
  2. 加入蘑菇;
  3. 加入小鸡炖蘑菇精粉;
  4. 煮汤;

声明“汤”对象:

public class Soup {
    /**
     * 材料1
     */
    private String cailiao1;
    /**
     * 材料2
     */
    private String cailiao2;
    /**
     * 材料3
     */
    private String cailiao3;

    //getter/setter省略
    @Override
    public String toString() {
        return "Soup{" +
                "cailiao1='" + cailiao1 + '\'' +
                ", cailiao2='" + cailiao2 + '\'' +
                ", cailiao3='" + cailiao3 + '\'' +
                '}';
    }
}    

人工做汤

    //人工做汤
    System.out.println("-----人工做汤-----");
    //制作小鸡炖蘑菇汤
    System.out.println("-----制作小鸡炖蘑菇-----");
    Soup xiaojimoguSoup = new Soup();
    xiaojimoguSoup.setCailiao1("小鸡儿");
    xiaojimoguSoup.setCailiao2("蘑菇");
    xiaojimoguSoup.setCailiao3("小鸡炖蘑菇精粉");
    System.out.println("小鸡炖蘑菇:"+xiaojimoguSoup);
    //制作海带排骨汤
    System.out.println("-----制作海带排骨汤-----");
    Soup haidaipaiguSoup = new Soup();
    haidaipaiguSoup.setCailiao1("海带");
    haidaipaiguSoup.setCailiao2("排骨");
    haidaipaiguSoup.setCailiao3("海带排骨精粉");
    System.out.println("海带排骨汤:"+haidaipaiguSoup);

在这里插入图片描述

可以看到做汤的时候需要添加什么材料全靠我自己去操作,这无疑是很复杂且繁琐的。

机器做汤(使用Builder模式)

现在我购买了一个做汤的机器,可以做各种各样的汤,这取决于你给机器选择什么样的程序
在这里插入图片描述
做汤程序通用接口:

public interface SoupProgram {
    void step1();//步骤1
    void step2();//步骤2
    void step3();//步骤3
    Soup make();//做汤
}

做小鸡炖蘑菇的程序:

public class XiaojimoguSoupProgram implements SoupProgram {
    private Soup soup = new Soup();
    @Override
    public void step1() {
        soup.setCailiao1("小鸡儿");
    System.out.println("加入小鸡儿");
    }

    @Override
    public void step2() {
        soup.setCailiao2("蘑菇");
    System.out.println("加入蘑菇");
    }

    @Override
    public void step3() {
        soup.setCailiao3("小鸡炖蘑菇精粉");
    System.out.println("加入小鸡炖蘑菇精粉");
    }

    @Override
    public Soup make() {
    System.out.println("小鸡炖蘑菇汤制作好啦~");
    return soup;
    }
}

做海带排骨汤程序:

public class HaidaipaiguSoupProgram implements SoupProgram {
    private Soup soup = new Soup();
    @Override
    public void step1() {
        soup.setCailiao1("海带");
    System.out.println("加入海带");
    }

    @Override
    public void step2() {
        soup.setCailiao2("排骨");
    System.out.println("加入排骨");
    }

    @Override
    public void step3() {
        soup.setCailiao3("海带排骨精粉");
    System.out.println("加入海带排骨精粉");
    }

    @Override
    public Soup make() {
    System.out.println("海带排骨汤制作好啦~");
    return soup;
    }
}

做汤机器:

public class SoupMakeMachine {
    public static Soup builder(SoupProgram program) {
        program.step1();
        program.step2();
        program.step3();
        return program.make();
    }
}

现在我只需要选择使用哪个做汤程序就可以用机器来完成做汤了。

    //机器做汤
    System.out.println("-----机器做汤-----");
    //制作小鸡炖蘑菇汤
    System.out.println("-----制作小鸡炖蘑菇-----");
    SoupProgram program = new XiaojimoguSoupProgram();
    System.out.println("小鸡炖蘑菇:"+SoupMakeMachine.builder(program));
    //制作海带排骨汤
    System.out.println("-----制作海带排骨汤-----");
    SoupProgram program1 = new HaidaipaiguSoupProgram();
    System.out.println("海带排骨汤:"+SoupMakeMachine.builder(program1));

在这里插入图片描述

一个更常用的变形

项目中经常使用OKHttpClient,里面构建OKHttpClient对象的时候经常会用类似于这样:

OkHttpClient.newBuilder().connectTimeout(10000)
.readTimeout(10000)
.writeTimeout(10000)
.build();

查看源码发现有这样一句:

public OkHttpClient.Builder newBuilder() {
    return new OkHttpClient.Builder(this);
}

其实它内部定义了一个静态内部类,这也是Builder模式变种的核心。

还是上面那个例子,改一下Soup类:

public class Soup {
    /**
     * 材料1
     */
    private String cailiao1;
    /**
     * 材料2
     */
    private String cailiao2;
    /**
     * 材料3
     */
    private String cailiao3;
    private Soup(Builder builder) {
        this.cailiao1 = builder.cailiao1;
        this.cailiao2 = builder.cailiao2;
        this.cailiao3 = builder.cailiao3;
    }
    //builder创建器
    public static Soup.Builder newBuilder() {
        return new Soup.Builder();
    }
    //静态内部类
    public static class Builder {
        /**
         * 材料1
         */
        private String cailiao1;
        /**
         * 材料2
         */
        private String cailiao2;
        /**
         * 材料3
         */
        private String cailiao3;
        public Builder cailiao1(String s) {
            System.out.println("加入"+s);
            this.cailiao1 = s;
            return this;
        }
        public Builder cailiao2(String s) {
            System.out.println("加入"+s);
            this.cailiao2 = s;
            return this;
        }
        public Builder cailiao3(String s) {
            System.out.println("加入"+s);
            this.cailiao3 = s;
            return this;
        }
        //生成目标对象
        public Soup build() {
            return new Soup(this);
        }
    }
    ...
}

使用这种方式创建对象

        System.out.println("-----制作小鸡炖蘑菇-----");
        Soup soup = Soup.newBuilder().cailiao1("小鸡儿")
                .cailiao2("蘑菇")
                .cailiao3("小鸡炖蘑菇精粉")
                .build();
        System.out.println("小鸡炖蘑菇:"+soup);
        System.out.println("-----制作海带排骨汤-----");
        Soup soup1 = Soup.newBuilder().cailiao1("海带")
                .cailiao2("排骨")
                .cailiao3("海带排骨精粉")
                .build();
        System.out.println("海带排骨汤:"+soup1);

在这里插入图片描述

可以看到这种方式创建对象明显感觉很方便而且很连贯。
这也正解决了使用多个重载构造器创建对象的繁琐,以及使用setter创建对象的不完整性(setter是先创建对象再赋值),属于先赋值再创建对象的方式。

优缺点

优点

1.隐藏对象的创建过程,将对象创建过程和对象本身解耦,使用者只需要关心要创建什么样的东西。通过这个例子人工和机器做汤的比较,很明显能发现使用机器能方便很多。现实生活中也是如此(如工厂流水线)。

2.方便扩展。上面也提到了,机器做汤,做什么样的汤取决于选择什么样的程序,如果你想做玉米萝卜汤,那你只需要给机器植入做玉米萝卜汤的程序(即实现SoupProgram接口)。

3.遵循了“开闭原则”。对象应该对扩展开放对修改关闭。

缺点

通过上面的代码,很明显能感觉到,使用这个Builder设计模式以后引入了更多的类和接口,增加了复杂度。


你可以在这里获取相关代码:设计模式-Builder模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值