10分钟搞懂建造者模式

引言

女娲造人🤏的时候,可能先捏个头,再捏个手,再捏个脚;也有可能先捏个脚,再捏个头,再捏个手。顺序是可变的,组件都一样,出来的可能是老人,男人,女人等等。

建造者模式的定义也是这样理解的,将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示,定义很花里胡哨,可以参考一下上面女娲造人的例子,就可以很快理解了。

使用场景

如果一个对象内部结构复杂,并且想把复杂对象的创建和使用分离。再具体点来说,就当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,就可以考虑使用构造者模式。

建造者模式的优点也很显而易见,封装性好、创建和使用分离,拓展性好、各个建造类之间独立、一定程度上解耦,缺点就是使用的时候,产生多余的 Builder 对象,一旦产品内部发生变化,建造者需要修改成本较大

Coding

我们来举一个简单的做菜例子,这边就简单举例,炒菜需要用到 调味料和食物,重写一下 toString 方法方便我们打印

public class Dishes {

    /**
     * 调味料
     */
    private String condiment;

    /**
     * 主料
     */
    private String food;

    @Override
    public String toString() {
        return "Dishes{" + "condiment='" + condiment + '\'' + ", food='" + food + '\'' + '}';
    }
}
复制代码

我们需要一个内部类,去实现把调味料和食物放入我们的锅中

    /**
     * 静态内部类
     */
    public static class FoodBuilder {

        /**
         * 调味料
         */
        private String condiment;

        /**
         * 主料
         */
        private String food;

        public FoodBuilder buildCondiment(String condiment) {
            this.condiment = condiment;
            return this;
        }

        public FoodBuilder buildFood(String food) {
            this.food = food;
            return this;
        }

        public Dishes build() {
            return new Dishes(this);
        }
    }
复制代码

这里需要特别注意的是,我们的 buildCondimentbuildFood 是一个链式调用,所以返回的都是FoodBuilder这个类,最后需要一个 build方法,把我们的菜搞出来

最后再实现一个构造器,将我们的FoodBuilder传进去

    private Dishes(FoodBuilder foodBuilder) {
        /**
         * 调味料
         */
        this.condiment = foodBuilder.condiment;
        /**
         * 主料
         */
        this.food = foodBuilder.food;
    }
复制代码

大功告成

public class Dishes {

    /**
     * 调味料
     */
    private String condiment;

    /**
     * 主料
     */
    private String food;

    @Override
    public String toString() {
        return "Dishes{" + "condiment='" + condiment + '\'' + ", food='" + food + '\'' + '}';
    }

    private Dishes(FoodBuilder foodBuilder) {
        /**
         * 调味料
         */
        this.condiment = foodBuilder.condiment;
        /**
         * 主料
         */
        this.food = foodBuilder.food;
    }

    /**
     * 静态内部类
     */
    public static class FoodBuilder {

        /**
         * 调味料
         */
        private String condiment;

        /**
         * 主料
         */
        private String food;

        public FoodBuilder buildCondiment(String condiment) {
            this.condiment = condiment;
            return this;
        }

        public FoodBuilder buildFood(String food) {
            this.food = food;
            return this;
        }

        public Dishes build() {
            return new Dishes(this);
        }
    }
}
复制代码

宁为代码熬通宵,不让bug点提交,养成良好的单测搞起来

public class Test {
    public static void main(String[] args) {
        Dishes dishes = new Dishes.FoodBuilder().buildFood("猪肉").buildCondiment("辣椒").build();
        System.out.println(dishes);
    }
}

Dishes{condiment='辣椒', food='猪肉'}

Process finished with exit code 0
复制代码

ok,非常 nice,辣椒炒肉做好了

框架运用

建造者模式在框架中的运用非常广泛,我们经常使用的 lombok ,StringBulider 都是建造者模式的实践

lombok

@Data
@Builder
public class User {
 
    private Long id;
 
    private String name;
}

public class TestLombok {
    public static void main(String[] args) {
        
        User user = User.builder().id(1L).build();
        
        System.out.println(user);
    }
}
复制代码

StringBulider

StringBulider 中也是非常经典的建造者模式的实现,我们可以聚焦到 129 -138 行

    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
复制代码

我们可以清楚看到 append 方法返回的是 StringBuilder 这个类

当然还有许多框架中藏着建造者模式的影子

Spring 中的建造者模式有

  • BeanDefinitionBuilder
  • UriComponents

Mybatis中的

  • SqlSessionFactoryBuilder
  • mybatis gennerator生成的Example对象

大家都可以去研究一下 🧐

那么本期就到此结束了,谨思慎言,我是 Skow

点个赞再走吧,我们下期再见

image.png