详解建造者(builder)模式的创建对象使用方式

目录

  • 抽象类代码
    • 导入依赖
    • Pizza类
    • Topping 枚举
    • 成员变量
    • 内部抽象类 Builder
    • Builder 类的成员变量
    • addTopping 方法:
    • build 方法:
    • self 方法:
    • Pizza 类的构造函数:
  • 实现类代码
    • 1. 导入Objects类
    • 2. NyPizza类
    • 3. Calzone类
    • 递归泛型类型
    • static:
    • abstract:
    • <T extends Builder<T>>:

抽象类代码

// Builder pattern for class hierarchiesimport java.util.EnumSet;
import java.util.Objects;
import java.util.Set;public abstract class Pizza {public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}final Set<Topping> toppings;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();}abstract Pizza build();// Subclasses must override this method to return "this"protected abstract T self();}Pizza(Builder<?> builder) {toppings = builder.toppings.clone(); // See Item 50}
}

这段代码展示了如何使用建造者(Builder)模式来构建具有层次结构的类,特别是用于创建Pizza对象,其中Pizza可以具有多种Topping。下面我将详细解释代码的每一部分:

导入依赖

import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;

这里导入了EnumSet(一个用于枚举类型的集合类)、Objects(包含一些静态工具方法,如requireNonNull)和Set接口。

Pizza类

public abstract class Pizza {// ...
}

Pizza`是一个抽象类,代表一个披萨。由于它是抽象的,你不能直接实例化它,但你可以创建它的子类。

Topping 枚举

public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}

这是一个枚举类型,列出了披萨上可能有的配料。

成员变量

final Set<Topping> toppings;

Pizza类有一个final的Set类型的成员变量toppings,用于存储披萨上的配料。因为它是final`的,所以它只能在构造函数中被初始化,并且之后不能再被更改。

内部抽象类 Builder

abstract static class Builder<T extends Builder<T>> {// ...
}

这是Pizza类中的一个静态内部抽象类,用于构建Pizza对象。这个类使用了泛型T,它是Builder类型的一个子类型,这允许我们在方法内部返回当前对象的类型(也称为“泛型自引用”或“递归泛型”)。

Builder 类的成员变量

EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);

Builder类有一个EnumSet类型的成员变量toppings,用于存储用户在构建披萨时选择的配料。开始时,它是空的(noneOf(Topping.class)`)。

addTopping 方法:

public T addTopping(Topping topping) {toppings.add(Objects.requireNonNull(topping));return self();
}

这个方法允许用户向披萨添加配料。它使用Objects.requireNonNull来确保传入的配料不为null。然后,它将配料添加到toppings集合中,并返回当前Builder对象(通过调用self()方法)。这允许链式调用,如

new Pizza.Builder().addTopping(Topping.HAM).addTopping(Topping.MUSHROOM).build()

build 方法:

 
abstract Pizza build();

这是一个抽象方法,子类必须实现它。该方法应该使用toppings集合中的信息来创建并返回一个新的Pizza对象。

self 方法:

protected abstract T self();

这是一个受保护的抽象方法,子类必须实现它。该方法应该返回当前Builder对象的类型(即T)。这是实现链式调用的关键。

Pizza 类的构造函数:

Pizza(Builder<?> builder) {toppings = builder.toppings.clone();
}

Pizza类的构造函数接受一个Builder对象作为参数。由于我们使用了?通配符,所以它可以接受任何类型的Builder对象。在构造函数内部,我们使用clone方法来复制builder.toppings集合,以确保Pizza对象的toppings`成员变量是独立的,并且不会被外部修改。

注意:这个代码片段只展示了Pizza类和Builder类的结构,但没有展示如何创建Pizza的子类或如何实际使用Builder来构建Pizza对象。在实际应用中,你还需要创建Pizza的子类并实现Builder的子类,以及可能的自定义build和self方法。

实现类代码

import java.util.Objects;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;}
}public class Calzone extends Pizza {private final boolean sauceInside;public static class Builder extends Pizza.Builder<Builder> {private boolean sauceInside = false; // Defaultpublic 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中使用构建者模式(Builder
Pattern)来创建具有不同属性和配置的Pizza类及其子类的实例。构建者模式是一种创建型设计模式,它允许通过链式方法调用来构建复杂对象。这种模式特别适用于那些有许多配置选项的对象。下面是对这段代码的详细解释:

1. 导入Objects类

import java.util.Objects;

这行代码导入了java.util.Objects类,这个类提供了一些静态的实用方法,比如requireNonNull,用于检查给定的对象引用是否为null。

2. NyPizza类

NyPizza类继承自Pizza类,并添加了一个枚举类型的属性size,表示披萨的大小。

枚举类型Size:定义了披萨的三个可能大小(SMALL、MEDIUM、LARGE)。

内部静态类Builder:这是构建者模式的核心。Builder类继承自Pizza.Builder,这表明Pizza类有一个泛型构建者类,用于构建Pizza及其子类的实例。

构造函数:Builder类的构造函数接受一个Size参数,并使用Objects.requireNonNull确保该参数不为null。
build方法:覆盖Pizza.Builder中的build方法,用于创建NyPizza的实例。
self方法:这是一个受保护的方法,返回当前构建器的实例,用于链式调用。
私有构造函数:NyPizza有一个私有构造函数,接受一个Builder对象作为参数,用于初始化披萨的大小。这个构造函数通过调用父类的构造函数(假设父类Pizza有一个接受Pizza.Builder的构造函数)并设置披萨的大小来初始化对象。

3. Calzone类

Calzone类也继承自Pizza类,但添加了一个布尔类型的属性sauceInside,表示酱料是否包裹在面团内部。

内部静态类Builder:与NyPizza类似,Calzone也有一个内部静态Builder类,但它有自己的sauceInside属性,并提供了sauceInside方法来设置这个属性。

默认构造函数:Builder类的默认构造函数将sauceInside设置为false。
sauceInside方法:一个公开的方法,用于设置sauceInside属性为true,并返回构建器实例,支持链式调用。
build和self方法:与NyPizza.Builder中的方法类似,但用于创建Calzone实例。
私有构造函数:Calzone的私有构造函数接受一个Builder对象作为参数,用于初始化酱料是否在面团内部以及(通过调用父类构造函数)可能的其他属性。

总结
这段代码通过构建者模式提供了一种灵活且类型安全的方式来创建Pizza及其子类的实例。每个子类都有一个对应的Builder内部静态类,用于配置该子类特有的属性。通过这种方式,可以创建具有不同配置和属性的披萨实例,同时保持代码的清晰和可维护性。

递归泛型类型

abstract static class Builder<T extends Builder<T>> 

是一个在 Java
中常见的模式,特别是在构建者(Builder)模式中使用泛型时。这个模式的主要目的是提供一个灵活且类型安全的方式来创建和配置复杂对象的实例。下面我将详细解释这个声明的每个部分:

static:

这意味着 Builder 类是一个静态嵌套类。静态嵌套类可以独立于其外部类被实例化,并且它不会持有对外部类实例的隐式引用。
在构建者模式中,Builder 通常被设计为静态的,因为它不依赖于其外部类的任何特定实例,并且它应该能够独立地创建对象。

abstract:

这意味着 Builder 类是一个抽象类,不能直接实例化。通常,你会提供一个或多个非抽象的子类或内部方法来创建 Builder
的实例,并可能提供一些默认实现。 在构建者模式中,Builder
类通常是抽象的,因为它定义了一个接口或契约,该接口或契约描述了如何配置和构建对象。具体的实现细节(例如,设置特定的字段值)可能在子类中定义。

<T extends Builder>:

这是一个泛型声明,它引入了类型参数 T。 T extends Builder 是一个类型约束,它要求 T 必须是 Builder
的一个子类(或 Builder 本身),并且该子类(或
Builder)必须使用它自己(或它的某个子类)作为类型参数。这种技巧被称为“递归泛型类型”。 这种递归泛型类型的使用允许你在
Builder 的方法内部返回 Builder 的正确类型,从而实现流畅的接口(fluent
interface)或链式调用(chaining)。

例如:

public class ComplexObject {private final String field1;private final int field2;private ComplexObject(Builder<?> builder) {this.field1 = builder.field1;this.field2 = builder.field2;}public static abstract class Builder<T extends Builder<T>> {protected String field1;protected int field2;public T setField1(String field1) {this.field1 = field1;return self();}public T setField2(int field2) {this.field2 = field2;return self();}// 这个方法返回“this”的当前类型(T),允许链式调用protected abstract T self();public ComplexObject build() {return new ComplexObject(this);}}// 提供一个具体的子类,以提供 self() 方法的实现public static class ConcreteBuilder extends Builder<ConcreteBuilder> {@Overrideprotected ConcreteBuilder self() {return this;}}// 使用示例public static void main(String[] args) {ComplexObject obj = new ConcreteBuilder().setField1("value1").setField2(42).build();}
}

在上面的示例中,ConcreteBuilder 是 Builder 的一个具体子类,它覆盖了 self() 方法并返回 this
的当前类型(ConcreteBuilder),从而允许链式调用。

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

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

相关文章

C++ 类型转换 包括C风格的转换、static_cast、const_cast、reinterpret_cast、dynamic_cast、模板特化等

C 类型转换 包括C风格的转换、static_cast、const_cast、reinterpret_cast、dynamic_cast、模板特化等 flyfish 0. 隐式转换&#xff08;Implicit Conversions&#xff09; 隐式转换是编译器自动进行的类型转换&#xff0c;通常在需要将一个类型转换为另一个类型以匹配函数参…

kafka架构+原理+源码

1.安装jdk17 sudo yum -y update sudo wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.rpm sudo yum -y install ./jdk-17_linux-x64_bin.rpm、 sudo java -version 2.安装kafka How to easily install kafka without zookeeper | Aditya’s Blog …

负载均衡工具haproxy完整安装部署流程 及 haproxy通过域名匹配进行负载-基于域名负载均衡的Haproxy配置

一、负载均衡工具haproxy完整安装部署流程 1. 关于负载均衡和haproxy 负载均衡是系统设计最常见的一种方式&#xff0c;Nginx、HAProxy、LVS、F5用得比较普遍&#xff0c;不过Nginx只能在HTTP层负载&#xff0c;而HAProxy即可以在7层做负载&#xff0c;也可以在4层做负载&…

一文了解AOL算子加速库

过去一年&#xff0c;随着ChatGPT的发布与快速迭代&#xff0c;基于大数据量、大参数量、大算力的预训练大模型已成为人工智能产业的主要路线。大模型的普及与发展不仅依靠模型本身的创新&#xff0c;更依赖于算力底座的支撑以及软件生态的繁荣&#xff0c;需要伙伴和开发者的积…

Vue3逻辑复用及内置组件

Vue3的逻辑复用主要通过“组合式函数”、“自定义指令”及“插件”来实现。提高了代码复用性&#xff0c;增强代码可维护性及促进团队合作。 1 逻辑复用 1.1 组合式函数 利用Vue组合式API来封装和复用有状态逻辑的函数。对组合式函数有如下约定&#xff1a; 命名&#xff0…

Sentinel限流规则详解

上一期教程讲解了 Sentinel 的快速入门&#xff1a;Sentinel快速入门&#xff0c;这一期主要讲述 Sentinel 的限流规则 簇点链路 簇点链路就是项目内的调用链路&#xff08;Controller -> Service -> Mapper&#xff09;&#xff0c;链路中被监控的每个接口就是一个资源…

wkhtmltopdf 工具安装与使用

前情提要&#xff1a; 最近一个同事请叫我一个问题&#xff0c;他发现一片不错的博文&#xff0c;是在博客园的&#xff0c;但是不能下载这篇文章&#xff0c;我看了一下才发现&#xff0c;原来csdn也是不行的。合理。毕竟是人家辛苦写的文章&#xff0c;不能就这么被别人随便c…

动态多态——java

多态&#xff08;Polymorphism&#xff09;是面向对象编程&#xff08;OOP&#xff09;的一个核心概念&#xff0c;它允许一个接口被多个类实现&#xff0c;从而使得一个方法调用可以根据对象的实际类型表现出不同的行为。多态性提高了代码的灵活性和可扩展性。 多态的类型 编译…

机器学习中的预测:真的是预测吗?

预测 机器学习中的预测&#xff1a;真的是预测吗&#xff1f;预测的定义为什么预测需要X&#xff1f;传统预测与机器学习预测的对比未来的预测模型&#xff1a;向自动生成输入的方向发展结论 机器学习中的预测&#xff1a;真的是预测吗&#xff1f; 在探讨机器学习中的“预测”…

《Java初阶数据结构》----7.<优先级队列PriorityQueue>

前言 大家好&#xff0c;我目前在学习java。之前也学了一段时间&#xff0c;但是没有发布博客。时间过的真的很快。我会利用好这个暑假&#xff0c;来复习之前学过的内容&#xff0c;并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区…

[渗透测试] 主动信息收集

主动信息收集 在红蓝对抗过程中&#xff0c;资产属于核心地位&#xff0c;攻击方&#xff08;红方&#xff09;要尽可能的去获取对方资产&#xff0c;暴露目标资产&#xff0c;包括IP地址、网络设备、安全设备、服务器、存储在服务器中的数据等。防守方也要清楚自己有多少有价…

宝塔SSL续签失败

我有2个网站a和b&#xff08;文字中用baidu.com替换我的域名&#xff09; b是要续签那个&#xff0c;但续签报错&#xff1a; nginx version: nginx/1.22.1 nginx: [emerg] host not found in upstream "github.com" in /www/server/panel/vhost/nginx/proxy/a.bai…

设计模式--创建型

实现 #include <iostream> #include <memory>// 抽象产品类 class Product {public:virtual ~Product() {}virtual void Operation() const 0; };// 具体产品 类A class ConcreteProductA : public Product {public:virtual void Operation() const override {st…

个人电脑网络安全 之 防浏览器和端口溢出攻击 和 权限对系统的重要性

防浏览器和端口溢出攻击 该如何防 很多人都不明白 我相信很多人只知道杀毒软件 却不知道网络防火墙 防火墙分两种 &#xff1a; 1、 病毒防火墙 也就是我们说的杀毒软件 2、 网络防火墙 这是用来防软件恶意通信的 使用防火墙 有两种 1、 半开式规则…

ESP-01S、ESP8266、ESP32等模块通信乱码的排查方法

ESP-01S、ESP8266、ESP32等模块通信乱码的排查方法 ESP-01S模块 遇到问题首先按重要顺序及排除法来解决问题 1&#xff0c;你的USB转串口工具是否有问题&#xff1f; 请将TXD与RXD短接在一起进行测试&#xff0c;自收自发应该是正常的&#xff0c;请确保这点。 ESP-01S的串口…

ffmpeg ffplay.c 源码分析二:数据读取线程

本章主要是分析 数据读取线程read_thread 中的工作。如上图红色框框的部分 从ffplay框架分析我们可以看到&#xff0c;ffplay有专⻔的线程read_thread()读取数据&#xff0c; 且在调⽤av_read_frame 读取数据包之前需要做&#xff1a; 1.例如打开⽂件&#xff0c; 2.查找配置解…

[Java 后端面试题]2024-7-22

Redis的持久化机制,在真实的线上环境中需要采取什么样的策略 在真实的线上环境中&#xff0c;Redis的持久化机制主要有两种&#xff1a;RDB&#xff08;Redis DataBase&#xff09;和AOF&#xff08;Append Only File&#xff09;。每种机制都有其优点和适用场景&#xff0c;实…

Servlet 3.0的新特征

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhlServlet 3.0概述 Servlet 3.0规范是在2009年随着Java EE 6的发布而推出的。它引入了一系列新特性和改进,旨在简化Web应用的开发和部署过程,并提高Web应用的性能和可扩展性。Servlet 3.0的发布标…

ProGuard配置详解

ProGuard是一个开源的Java class文件缩小器、优化器、混淆器和预验证器。它通过删除未使用的类、字段、方法和属性&#xff0c;优化字节码指令&#xff0c;并重命名类、字段和方法&#xff0c;使反编译后的代码难以理解&#xff0c;从而提高应用的安全性。以下是对ProGuard配置…

大语言模型-对比学习-Contrastive Learning

一、对比学习概念 对比学习是一种特殊的无监督学习方法。 旨在通过拉近相关样本的距离并且推远不相关样本的距离&#xff0c;来学习数据表示。 通常使用一种高自由度、自定义的规则来生成正负样本。在模型预训练中有着广泛的应用。 二、对比学习小案例 对比学习主要分为三个…