建造者模式 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();
相关推荐
TT-Kun3 天前
设计模式 之 建造者模式(C++)
c++·设计模式·建造者模式
依恋、阳光5 天前
建造者模式构建对象
java·设计模式·建造者模式·类与对象
wy02_8 天前
【设计模式】 建造者模式和原型模式
设计模式·建造者模式·原型模式
ox008010 天前
C++ 设计模式-建造者模式
c++·设计模式·建造者模式
小王子102425 天前
设计模式Python版 建造者模式
python·设计模式·建造者模式
找了一圈尾巴1 个月前
设计模式-建造者模式、原型模式
设计模式·建造者模式
等一场春雨1 个月前
Java设计模式 五 建造者模式 (Builder Pattern)
java·设计模式·建造者模式
3D小将1 个月前
3D 模型格式转换之 STP 转 STL 深度解析
3d·建造者模式
运筹帷幄小红花1 个月前
建造者模式(或者称为生成器(构建器)模式)
建造者模式
Leaf吧1 个月前
java 设计模式 建造者模式
java·设计模式·建造者模式