如何使用建造者模式构建复杂对象?

『建造者模式』是一种简化复杂对象构建过程的设计模式,他的核心夙愿是:把对象的构建和表述分离java

举个栗子

每种食品包装上都会有一个养分成分表,每份的含量、每罐的含量、每份卡路里、脂肪、碳水化合物、钠等,还可能会有其余 N 种可选数据,大多数产品的某几个成分都有值git

那么咱们表述这个食品类:程序员

public class Nutrition {
    private int servingSize;// required
    private int servings;// required
    private int calories;// optional
    private int fat;// optional
    private int sodium;// optional
    private int carbohydrate;// optional

    public Nutrition(int servingSize,int servings) {
        //.....
    }

    public Nutrition(int servingSize,int servings, int calories) {
        //.....
    }

    public Nutrition(int servingSize,int servings,int calories,int fat) {
        //.....
    }

    public Nutrition(int servingSize, int servings,int calories, int fat,int sodium) {
        //.....
    }

    public Nutrition(int servingSize,int servings,int calories,int fat,int sodium,int carbohydrate) {
        //.....
    }
}
复制代码

这里的字段还很少,构造一个对象已经如此复杂了,可想而知,真实业务中十几二十个属性该如何构建?github

有人说,只使用构造函数传递必须参数,可选参数经过 setter 方法调用传入。不错,这种方式应该也是你们业务中处理的方式吧,没别的,『建造者模式』仅仅表示,这种构造函数+setter方法的 方式不够优雅。面试

建造者模式

定义一个抽象 Builder:设计模式

public abstract class AbstractBuilder {

    protected Nutrition nutrition;

    public AbstractBuilder setServingSizeServings(int size,int servings){
        nutrition.setServingSize(size);
        nutrition.setServings(servings);
        return this;
    }
    public AbstractBuilder setCalories(int calories){
        nutrition.setCalories(calories);
        return this;
    }
    public AbstractBuilder setFat(int fat){
        nutrition.setFat(fat);
        return this;
    }
    public AbstractBuilder setSodium(int sodium){
        nutrition.setSodium(sodium);
        return this;
    }
    public AbstractBuilder setCarbohydrate(int carbohydrate){
        nutrition.setCarbohydrate(carbohydrate);
        return this;
    }

    public Nutrition build(){
        return nutrition;
    }
}
复制代码

定义一个默认实现的 Builder:微信

public class DefaultBuilder extends AbstractBuilder{
}
复制代码

客户端构建一个对象:markdown

Nutrition nutrition = new DefaultBuilder().
    setServingSizeServings(10, 20).
    setCalories(100).
    build();
复制代码

建造者模式实现完了。你会发现 Nutrition 对象的表述和他的构造是彻底分离的。mybatis

至于和构造函数+setter方式有什么区别,我想比较重要的一点区别就是,setter 方法能够被任意调用,你没法准确断定对象初始化生成时候的初始参数值是什么,使用构造者就会比较明显,构造这个对象使用了哪些参数,一目了然。app

而且,我这里只提供了一个默认 DefaultBuilder,若是你有特殊需求,你能够自定义实现一个 Builder,设置他的某些字段值为一个固定值,这样 build 出来的对象在某些属性上就是固定的,是一种特殊对象。

哪些源码在实践

一、JDK 中的 StringBuilder、StringBuffer最显而易见了,他们的目标是建造一个 String 对象,建造的方法就是 toString 方法,经过各类 append 方法 “参数化” 对象。

二、mybatis 中的 SqlSessionFactoryBuilder

三、SpringMVC 中的 UriComponentsBuilder

你还知道哪些在使用建造者模式的优秀框架?


关注公众不迷路,一个爱分享的程序员。

公众号回复「1024」加做者微信一块儿探讨学习!

公众号回复「面试题」送你一份面试题以及做者的做答答案

每篇文章用到的全部案例代码素材都会上传我我的 github

github.com/SingleYam/o…

欢迎来踩!

相关文章
相关标签/搜索