建造者模式 Builder Pattern

news2025/1/2 6:36:05

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

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

传统方式

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

@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);
        }
    }
}

对象创建过程如下:

    @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 一起用。

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;
}

创建过程:

    @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对象 代码。

和传统方式并为区别。

    @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方法。

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上铺的食材(火腿、蘑菇、洋葱、辣椒、香肠之类的)。

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。

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 代表“是否内部有酱汁”。

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;
    }
}

创建过程如下:

        NyPizza pizza = new NyPizza.Builder(SMALL)
                .addTopping(SAUSAGE).addTopping(ONION).build();
        Calzone calzone = new Calzone.Builder()
                .addTopping(HAM).sauceInside().build();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2267936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java】IO流练习

IO流练习 题干&#xff1a; 根据指定要求&#xff0c;完成电话记录、 注册、登录 注册 题干&#xff1a; 完成【注册】功能&#xff1a; 要求&#xff1a; 用户输入用户名、密码存入users.txt文件中 若users.txt文件不存在&#xff0c;创建该文件若users.txt文件存在 输入…

计算机网络:应用层 —— 网络应用模式

文章目录 客户—服务器方式和对等方式客户/服务器方式 (C/S方式)工作流程特点 对等方式 (P2P方式)工作流程P2P 应用特点 客户—服务器方式和对等方式 网络应用程序运行在处于网络边缘的不同的端系统上&#xff0c;通过彼此间的通信来共同完成某项任务。 开发一种新的网络应用…

118.【C语言】数据结构之排序(堆排序和冒泡排序)

目录 1.堆排序 2.冒泡排序 单趟排序的两种情况 情况1.和arr[i]的前一个元素交换,第一次循环结束时i的值为n-1,第二次循环结束时i的值为n-2 情况2.和arr[i]的后一个元素交换,第一次循环结束时i的值为n-2,第二次第一次循环结束时i的值为n-3,... 将单趟排序代码嵌入外循环中…

【图像处理lec9】小波与多分辨率分析

目录 一、背景 1、引出小波变换 2、图像金字塔 &#xff08;1&#xff09;图像金字塔的基本概念 &#xff08;2&#xff09;高斯金字塔 &#xff08;3&#xff09;拉普拉斯金字塔 &#xff08;4&#xff09;金字塔的结构与生成框图 3、子带编码 &#xff08;1&#xf…

ubuntu2204 gpu 没接显示器,如何连接vnc

之前一直用ssh ,一直没接显示器&#xff0c;后来实在不方便&#xff0c;要安个vnc看一下&#xff0c;结果装上就黑了 硬件上&#xff1a;买一个HDMI显卡欺骗器插在设备上。 软件上&#xff1a;装一个虚拟显示器欺骗一下 sudo apt install xserver-xorg-coresudo apt install…

黑神话悟空游戏鼠标光标使用教程与下载

效果图&#xff1a; 鼠标光标特点 这套鼠标光标的设计灵感来源于《黑神话&#xff1a;悟空》游戏中的角色和元素&#xff0c;具有以下特点&#xff1a; • 主题鲜明&#xff1a;光标设计紧扣游戏主题&#xff0c;采用了游戏中的元素&#xff0c;让玩家在使用电脑时也能感受到…

32132132123

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

简述css中z-index的作用?如何用定位使用?

z-index是一个css属性&#xff0c;用于控制元素的堆叠顺序&#xff0c; 如何使用定位用index 1、position&#xff1a;relative&#xff1b; z-index&#xff1b; 相对于自己来定位的&#xff0c;可以根据top&#xff0c;bottom&#xff0c;right&#xff0c;left&#xff…

简单贪吃蛇小游戏的设计与实现

文章目录 1、知识预备1.1 WIN32 API1.1.1 什么是WIN32 API1.1.2 了解部分WIN32 API1.1.2.1 控制台坐标1.1.2.2 控制台光标1.1.2.3 获取键盘按键情况 2.1 宽字符2.1.1 C语言的国际化2.1.2 宽字符的打印 2、 贪吃蛇游戏设计2.1 游戏开始2.2 游戏运行2.2.1 更新分数2.2.2 按键检测…

mac中idea菜单工具栏没有git图标了

1.右击菜单工具栏 2.选中VCS&#xff0c;点击添加 3.搜索你要的工具&#xff0c;选中点击确定就添加了 4.回到上面一个界面&#xff0c;选中你要放到工具栏的工具&#xff0c;点击应用就好了 5.修改图标&#xff0c;快捷键或者右击选中编辑图标 6.选择你要的图标就好了

ElementPlus 自定义封装 el-date-picker 的快捷功能

文章目录 需求分析 需求 分析 我们看到官网上给出的案例如下&#xff0c;但是不太满足我们用户想要的快捷功能&#xff0c;因为不太多&#xff0c;因此需要我们自己封装一些&#xff0c;方法如下 外部自定义该组件的快捷内容 export const getPickerOptions () > {cons…

电子病历五级的Python编程基础实战

一、电子病历五级之路&#xff1a;机遇与挑战并存 在当今数字化医疗的浪潮下&#xff0c;电子病历五级成为医院迈向高质量发展的关键里程碑。它不仅象征着医院信息化建设的深度与广度&#xff0c;更是保障医疗服务质量、提升患者安全的核心要素。随着电子病历五级标准的深入推行…

VDA 学习手册

VDA&#xff08;Verband der Automobilindustrie&#xff0c;德国汽车工业联合会&#xff09;报文标准是专为汽车行业制定的电子数据交换&#xff08;EDI&#xff09;标准&#xff0c;用于支持供应链管理中的数据传输。它是由德国汽车工业联合会开发和维护的&#xff0c;广泛应…

cesium入门学习四

怎么加载地图效果文件&#xff0c;地图效果的显示。 学习总结&#xff1a; 1.cesium入门学习一-CSDN博客 2.cesium入门学习二-CSDN博客 3.cesium入门学习三-CSDN博客 1.怎么加载geojson文件&#xff0c;并在html中显示 1.1 geojson文件来源&#xff1a; DataV.GeoAtlas地理小…

前端开发 -- 自动回复机器人【附完整源码】

一&#xff1a;效果展示 本项目实现了一个简单的网页聊天界面&#xff0c;用户可以在输入框中输入消息&#xff0c;并点击发送按钮或按下回车键来发送消息。机器人会根据用户发送的消息内容&#xff0c;通过关键字匹配来生成自动回复。 二&#xff1a;源代码分享 <!DOCTYP…

2011-2019年各省总抚养比数据

2011-2019年各省总抚养比数据 1、时间&#xff1a;2011-2019年 2、来源&#xff1a;国家统计局 3、指标&#xff1a;行政区划代码、地区、年份、总抚养比(人口抽样调查)(%) 4、范围&#xff1a;31省 5、指标解释&#xff1a;总抚养比也称总负担系数。指人口总体中非劳动年…

【从零开始入门unity游戏开发之——C#篇34】C#匿名函数(delegate )和Lambda表达式

文章目录 一、匿名函数&#xff08;delegate &#xff09;1、什么是匿名函数&#xff1f;2、匿名函数的基本语法2.1 语法2.2 **没有参数的匿名函数&#xff1a;**2.3 **有参数的匿名函数&#xff1a;**2.4 **有返回值的匿名函数&#xff1a;** 3、匿名函数的使用示例3.1 作为参…

脱离电路图编程

SM0.0常开始终吸合 SM0.1&#xff08;特殊中继&#xff09; 常开&#xff1a;闭合一次再断开 常闭&#xff1a;断开一次再闭合 上述是依据电路图编程

人工智能及深度学习的一些题目

1、一个含有2个隐藏层的多层感知机&#xff08;MLP&#xff09;&#xff0c;神经元个数都为20&#xff0c;输入和输出节点分别由8和5个节点&#xff0c;这个网络有多少权重值&#xff1f; 答&#xff1a;在MLP中&#xff0c;权重是连接神经元的参数&#xff0c;每个连接都有一…

OpenGL变换矩阵和输入控制

在前面的文章当中我们已经成功播放了动画&#xff0c;让我们的角色动了起来&#xff0c;这一切变得比较有意思了起来。不过我们发现&#xff0c;角色虽然说是动了起来&#xff0c;不过只是在不停地原地踏步而已&#xff0c;而且我们也没有办法通过键盘来控制这个角色来进行移动…