建造者模式 Builder Pattern

在创建一个对象的时候,构造器参数有点多,而且有些参数还是可选的,再者还有不少同类型的,那就更应该使用 builder 模式了。

使用 Builder 模式的初衷是 把易变性(mutability)移动到Builder类,而使得要被创建的对象变得不可变(immutable)。

传统方式

创建静态内部类 Builder ,此 Builder 和外部类拥有一样的属性。包括一些返回 this对象的 setter方法,和一个 build 方法调用 外部类的 以builder对象为参数构造器 来创建 外部类对象。

java 复制代码
@Getter
public class Blog {
    private final String title;
    private final String content;
    private final String label;
    private final String author;

    public Blog(BlogBuilder builder){
        this.title = builder.title;
        this.content = builder.content;
        this.label = builder.label;
        this.author = builder.author;
    }

    static class BlogBuilder{
        private String title;
        private String content;
        private String label;
        private String author;

        BlogBuilder title(String title){
            this.title = title;
            return this;
        }
        BlogBuilder content(String content){
            this.content = content;
            return this;
        }
        BlogBuilder label(String label){
            this.label = label;
            return this;
        }
        BlogBuilder author(String author){
            this.author = author;
            return this;
        }
        Blog build(){
            return new Blog(this);
        }
    }
}

对象创建过程如下:

java 复制代码
    @Test
    public void test(){
        Blog blog = new Blog.BlogBuilder()
                .author("dachuili")
                .content("classic/generic/lombok builder pattern")
                .label("design patterns")
                .title("builder pattern")
                .build();
        assertSame(blog.getAuthor(), "dachuili");
        assertSame(blog.getTitle(), "builder pattern");
        assertSame(blog.getContent(), "classic/generic/lombok builder pattern");
        assertSame(blog.getLabel(), "design patterns");
    }

Lombok方式

Lombok提供了注解 @Builder ,一步实现 Builder 模式。不要忘了和@Value 一起用。

java 复制代码
import lombok.Builder;
import lombok.Data;
import lombok.Value;

@Builder
@Value
@Data
public class Article {
    private String title;
    private String content;
    private String label;
    private String author;
}

创建过程:

java 复制代码
    @Test
    public void test(){
        Article article = Article.builder()
                .author("dachuili")
                .content("classic/generic/lombok builder pattern")
                .label("design patterns")
                .title("builder pattern")
                .build();
        assertSame(article.getAuthor(), "dachuili");
        assertSame(article.getTitle(), "builder pattern");
        assertSame(article.getContent(), "classic/generic/lombok builder pattern");
        assertSame(article.getLabel(), "design patterns");
    }

是不是很 easy!很 convenient!

@Builder 帮我们创建了 Builder 对象以及 这些返回 this对象的 setter方法。以下是lombok生成的ArticleBuilder对象 代码。

和传统方式并为区别。

java 复制代码
    @Generated
    public static ArticleBuilder builder() {
        return new ArticleBuilder();
    }

    @Generated
    public static class ArticleBuilder {
        @Generated
        private String title;
        @Generated
        private String content;
        @Generated
        private String label;
        @Generated
        private String author;

        @Generated
        ArticleBuilder() {
        }

        @Generated
        public ArticleBuilder title(String title) {
            this.title = title;
            return this;
        }

        @Generated
        public ArticleBuilder content(String content) {
            this.content = content;
            return this;
        }

        @Generated
        public ArticleBuilder label(String label) {
            this.label = label;
            return this;
        }

        @Generated
        public ArticleBuilder author(String author) {
            this.author = author;
            return this;
        }

        @Generated
        public Article build() {
            return new Article(this.title, this.content, this.label, this.author);
        }
    }

通用的Builder

借助于 Java 8 提供的 Supplier和 BiConsumer 创建一个通用工具类,好玩是好玩,强行捏了一个builder出来,感觉违背了 builder模式的初衷,回到了 JavaBeans Pattern 那种setter方法。

java 复制代码
public class GenericBuilder<T> {
    private final Supplier<T> supplier;

    private GenericBuilder(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public static <T> GenericBuilder<T> of(Supplier<T> supplier) {
        return new GenericBuilder<>(supplier);
    }

    public <P> GenericBuilder<T> with(BiConsumer<T, P> consumer, P value) {
        return new GenericBuilder<>(() -> {
            T object = supplier.get();
            consumer.accept(object, value);
            return object;
        });
    }

    public T build() {
        return supplier.get();
    }
}    



Post post = GenericBuilder.of(Post::new)
                .with(Post::setTitle, "builder pattern")
                .with(Post::setAuthor, "dachuili")
                .with(Post::setLabel, "design patterns")
                .with(Post::setContent, "classic/generic/lombok builder pattern")
                .build();

Builder模式与抽象类

以下代码来自 Joshua Bloch 的《Effective Java》一书,抽象类有自己的Builder方法, 实现类有自己的 Builder方法。

首先是我们的抽象类 Pizza,定义了一些 topping,也就是Pizza上铺的食材(火腿、蘑菇、洋葱、辣椒、香肠之类的)。

java 复制代码
public abstract class Pizza {
    public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
    final Set<Topping> toppings;
    Pizza(Builder<?> builder) {
        toppings = builder.toppings.clone();
    }

    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }
        // Subclasses must override this method to return "this"
        protected abstract T self();

        abstract Pizza build();
    }
}

其中一个实现类是 NyPizza 和 自己的 Builder 实现类,New York Pizza 有不同的size。

java 复制代码
public class NyPizza extends Pizza{
    public enum Size { SMALL, MEDIUM, LARGE }
    private final Size size;

    public static class Builder extends Pizza.Builder<Builder>{
        private final Size size;
        public Builder(Size size) {
            this.size = Objects.requireNonNull(size);
        }
        @Override public NyPizza build() {
            return new NyPizza(this);
        }
        @Override protected Builder self() { return this; }
    }

    private NyPizza(Builder builder) {
        super(builder);
        size = builder.size;
    }
}

另一个实现类是 Calzone,搜了下就是这个菜盒子。🤣

不区分大小,但有个属性 sauceInside 代表"是否内部有酱汁"。

java 复制代码
public class Calzone extends Pizza{
    private final boolean sauceInside;
    public static class Builder extends Pizza.Builder<Builder>
    {
        private boolean sauceInside = false; // Default
        public Builder sauceInside() {
            sauceInside = true;
            return this;
        }
        @Override public Calzone build() {
            return new Calzone(this);
        }
        @Override protected Builder self() { return this; }

    }
    private Calzone(Builder builder) {
        super(builder);
        sauceInside = builder.sauceInside;
    }
}

创建过程如下:

java 复制代码
        NyPizza pizza = new NyPizza.Builder(SMALL)
                .addTopping(SAUSAGE).addTopping(ONION).build();
        Calzone calzone = new Calzone.Builder()
                .addTopping(HAM).sauceInside().build();
相关推荐
水宝的滚动歌词5 小时前
设计模式之建造者模式
java·设计模式·建造者模式
玉面小君4 天前
C# 设计模式(创建型模式):建造者模式
设计模式·c#·建造者模式
angen20184 天前
二十三种设计模式-建造者模式
设计模式·建造者模式
JINGWHALE15 天前
设计模式 创建型 建造者模式(Builder Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·算法·设计模式·性能优化·建造者模式
游客5205 天前
设计模式-建造者模式
开发语言·python·设计模式·junit·建造者模式
冀晓武7 天前
C++ 设计模式:建造者模式(Builder Pattern)
c++·设计模式·建造者模式
biubiubiu07068 天前
建造者模式
java·开发语言·建造者模式
shuair8 天前
java设计模式-创建型模式-建造者模式
java·设计模式·建造者模式
小马爱打代码10 天前
设计模式详解(建造者模式)
java·设计模式·建造者模式