创建和销毁对象——遇到多个构造器参数时要考虑使用构建器
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。比如用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份的含量、每罐的含量以及每份的卡路里。还有超过20个的可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。大多数产品在某几个可选域中都会有非零的值。
对于这样的类,应该用哪种构造器或者静态工厂来编写呢?程序员一向习惯采用重叠构造器模式,在这种模式下,提供的第一个构造器只有必要的参数,第二个构造器有一个可选参数,第三个构造器有两个可选参数,以此类推,最后一个构造器包含所有可选的参数。下面有个示例,为了简单起见,它只显示四个可选域:
final修饰的变量表示赋值之后不能再进行更改,系统赋默认值也算赋值,因此系统也不会赋默认值
/*** 营养成分*/
public class NutritionFacts {private final int servingSize; // 每份含量 requiredprivate final int servings; // 每罐含量 requiredprivate final int calories;// 卡路里/罐 optionalprivate final int fat;// 脂肪/罐 optionalprivate final int sodium; // 钠/罐 optionalprivate final int carbohydrate; // 碳水/罐 optionalpublic NutritionFacts(int servingSize, int servings) {this(servingSize, servings, 0);}public NutritionFacts(int servingSize, int servings, int calories) {this(servingSize, servings, calories, 0);}public NutritionFacts(int servingSize, int servings, int calories, int fat) {this(servingSize, servings, calories, fat, 0);}public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {this(servingSize, servings, calories, fat, sodium, 0);}public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {this.servingSize = servingSize;this.servings = servings;this.calories = calories;this.fat = fat;this.sodium = sodium;this.carbohydrate = carbohydrate;}
}
当你想要创建实例的时候,就利用参数列表最短的构造器,该列表中包含了要设置的所有参数:
NutritionFacts cocaCola = new NutritionFacts(240 ,8,100,0,35,27);
这个构造器调用通常需要许多你本不想设置的参数,但还是不得不为它们传递值。在这个例子中,我们给fat 传递了一个值为0 。 如果“仅仅”是这6个参数,看起来还不算太糟糕,问题是随着参数数目的增加,它很快就失去了控制。
简而言之,重叠构造器模式可行,但是当有很多参数的时候,客户端代码会很难编写,并且仍然很难阅读。如果读者想知道那些值是什么意思,必须很仔细地数着这些参数来探个究竟。一长串类型相同的参数会导致一些微妙的错误。如果客户端不小心颠倒了其中两个参数的顺序,编译器也不会出错,但是程序在运行时会出现错误的行为
遇到许多可选的构造器参数的时候,还有第二种代替办法,即JavaBeans模式,在这种模式下,取消掉final修饰符,先调用一个无参构造器来创建对象,然后再调用setter 方法来设置每个必要的参数,以及每个相关的可选参数
/*** 营养成分*/
public class NutritionFacts {private int servingSize; // 每份含量 requiredprivate int servings; // 每罐含量 requiredprivate int calories;// 卡路里/罐 optionalprivate int fat;// 脂肪/罐 optionalprivate int sodium; // 钠/罐 optionalprivate int carbohydrate; // 碳水/罐 optionalpublic int getServingSize() {return servingSize;}public void setServingSize(int servingSize) {this.servingSize = servingSize;}public int getServings() {return servings;}public void setServings(int servings) {this.servings = servings;}public int getCalories() {return calories;}public void setCalories(int calories) {this.calories = calories;}public int getFat() {return fat;}public void setFat(int fat) {this.fat = fat;}public int getSodium() {return sodium;}public void setSodium(int sodium) {this.sodium = sodium;}public int getCarbohydrate() {return carbohydrate;}public void setCarbohydrate(int carbohydrate) {this.carbohydrate = carbohydrate;}
}
这种模式弥补了重叠构造器模式的不足。说得明白一点,就是创建实例很容易,这样产生的代码读起来也很容易:
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
遗憾的是,JavaBeans 模式自身有着很严重的缺点。因为构造过程被分到了几个调用中, 在构造过程中,JavaBeans 可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象将会导致失败,这种失败与包含错误的代码大相径庭,因此调试起来十分困难。与此相关的另一点不足在于,JavaBeans 模式使得把类做成不可变的可能性不复存在,这就需要程序员付出额外的努力来确保它的线程安全。
幸运的是,还有第三种替代方法,它既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans 那么好的可读性。这就是建造者(Builder)模式的一种形式,它不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder 对象。然后客户端在builder 对象上调用类似于setter 的方法,来设置每个相关的可选参数。最后,客户端调用无参的build 方法来生成通常是不可变的对象。这个builder 通常是它构建的类的静态成员类,下面就是它的示例:
/*** 营养成分*/
public class NutritionFacts {private final int servingSize; // 每份含量 requiredprivate final int servings; // 每罐含量 requiredprivate final int calories;// 卡路里/罐 optionalprivate final int fat;// 脂肪/罐 optionalprivate final int sodium; // 钠/罐 optionalprivate final int carbohydrate; // 碳水/罐 optionalprivate NutritionFacts(Builder builder) {servingSize = builder.servingSize;servings = builder.servings;calories = builder.calories;fat = builder.fat;sodium= builder.sodium;carbohydrate = builder.carbohydrate;} public static class Builder{private int servingSize; // (ml) 每份含量 requiredprivate int servings; // (per container) 每罐含量 requiredprivate int calories;// (per serving) 卡路里/每罐 optionalprivate int fat;// (g/serving)脂肪 g/罐 optionalprivate int sodium; // (mg/serving) 钠 mg/罐 optionalprivate int carbohydrate; //(g/serving) 碳水 g/罐 optionalpublic Builder(int servingSize,int servings){this.servingSize = servingSize;this.servings = servings;}public Builder calories(int val){calories = val;return this;}public Builder fat(int val){fat = val;return this;}public Builder sodium(int val){sodium = val;return this;}public Builder carbohydrate(int val){carbohydrate = val;return this;}public NutritionFacts build(){return new NutritionFacts(this);}}
}
注意 NutritionFacts 是不可变的,所有的默认参数值都单独放在一个地方。builder的设置方法返回自身,以便把调用链接起来,得到一个流式API。下面就是其客户端代码:
NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();
这样的客户端代码很容易编写,更为重要的是易于阅读。Builder 模式模拟了可选参数
为了简洁起见,示例中省略了有效性检查。要想尽快侦测到无效的参数,可以在builder 的构造器和方法中检查参数的有效性。查看不可变量,包括build方法调用的构造器中的多个参数。为了确保这些不变量免受攻击,从builder 复制完参数之后,要检查对象域(详见第50条)。如果检查失败就抛出 IllegalArgumentException,其中的详细信息会说明哪些参数是无效的。
与构造器相比,builder 的微弱优势在于,它可以有多个可变(varargs)参数。因为builder 是利用单独的方法来设置每一个参数。
Builder 模式的确也有它自身的不足。为了创建对象,必须先创建它的构建器。虽然创建这个构建器的开销在实践中可能不那么明显,但是在某些十分注重性能的情况下,可能就成问题了。Builder 模式还比重叠构造器模式更加冗长,因此它只在有很多参数的时候才使用,比如4个或更多。但是记住,将来你可能需要添加参数。如果一开始就使用构造器或静态工厂,等到类需要多个参数时才添加构造器,就会无法控制,那些过时的构造器或者静态工厂显得十分不协调。因此,通常最好一开始就使用构建器(Builder)。
简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是一种不错的选择,特别是当大多数参数都是可选或者类型相同的时候。与使用重叠构造器模式相比,使用Builder 模式的客户端将更易于阅读和编写,构建器也比JavaBeans 更加安全
如果是内部调用较多,确定没有安全问题,JavaBean模式和builder 已经非常接近了,并且,可以使用链式调用的方式,让JavaBean 的调用显得更简洁,更像builder
/*** 营养成分*/
public class NutritionFacts {private int servingSize; // 每份含量 requiredprivate int servings; // 每罐含量 requiredprivate int calories;// 卡路里/罐 optionalprivate int fat;// 脂肪/罐 optionalprivate int sodium; // 钠/罐 optionalprivate int carbohydrate; // 碳水/罐 optionalpublic NutritionFacts setServingSize(int servingSize) {this.servingSize = servingSize;return this;}public NutritionFacts setServings(int servings) {this.servings = servings;return this;}public NutritionFacts setCalories(int calories) {this.calories = calories;return this;}public NutritionFacts setFat(int fat) {this.fat = fat;return this;}public NutritionFacts setSodium(int sodium) {this.sodium = sodium;return this;}public NutritionFacts setCarbohydrate(int carbohydrate) {this.carbohydrate = carbohydrate;return this;}public static void main(String[] args) {NutritionFacts cocaCola = new NutritionFacts().setServingSize(240).setServings(8).setCalories(100).setSodium(35).setCarbohydrate(27);}
}
当然,现在很多公司和团队已经开始积极使用Lombok 来简化bean 对象。可以如下
/*** 营养成分*/
@Getter
@Setter
@Accessors(chain = true)
public class NutritionFacts {private int servingSize; // 每份含量 requiredprivate int servings; // 每罐含量 requiredprivate int calories;// 卡路里/罐 optionalprivate int fat;// 脂肪/罐 optionalprivate int sodium; // 钠/罐 optionalprivate int carbohydrate; // 碳水/罐 optionalpublic static void main(String[] args) {NutritionFacts cocaCola = new NutritionFacts().setServingSize(240).setServings(8).setCalories(100).setSodium(35).setCarbohydrate(27);}
}相关文章:
创建和销毁对象——遇到多个构造器参数时要考虑使用构建器
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。比如用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份的含量、每罐的含量以及每份的卡路里。还有超过20个的可选域:总脂肪量、饱和脂…...
【c++学习】入门c++(中)
目录一. 前言二. 函数重载1. 概念2.函数名修饰规则三 .引用(&)1. 概念2. 引用特性3.应用1.做参数2. 做返回值3. 传值、传引用效率比较4.引用和指针的区别四 . 结语一. 前言 小伙伴们大家好,今天我们继续学习c入门知识,今天的…...
论文阅读_AlphaGo_Zero
论文信息 name_en: Mastering the game of Go without human knowledge name_ch: 在没有人类知识的情况下掌握围棋游戏 paper_addr: http://www.nature.com/articles/nature24270 doi: 10.1038/nature24270 date_publish: 2017-10-01 tags: [‘深度学习’,‘强化学习’] if: 6…...
一文教你用Python创建自己的装饰器
python装饰器在平常的python编程中用到的还是很多的,在本篇文章中我们先来介绍一下python中最常使用的staticmethod装饰器的使用。 目录一、staticmethod二、自定义装饰器python类实现装饰器python函数嵌套实现装饰器多个装饰器调用三、带参数的装饰器一、staticmet…...
华为OD机试 - 任务总执行时长(JS)
任务总执行时长 题目 任务编排服务负责对任务进行组合调度。参与编排的任务又两种类型,其中一种执行时长为taskA,另一种执行时长为taskB。任务一旦开始执行不能被打断,且任务可连续执行。服务每次可以编排num个任务。 请编写一个方法,生成每次编排后的任务所有可能的总执…...
pytorch离线快速安装
1.pytorch官网查看cuda版本对应的torch和torchvisionde 版本(ncvv -V,nvidia-sim查看cuda对应的版本) 2.离线下载对应版本,网址https://download.pytorch.org/whl/torch_stable.html 我下载的: cu113/torch-1.12.0%2Bcu113-cp37-cp37m-win_…...
华为OD机试 - 数组合并(JS)
数组合并 题目 现在有多组整数数组,需要将他们合并成一个新的数组。 合并规则,从每个数组里按顺序取出固定长度的内容合并到新的数组中, 取完的内容会删除掉, 如果该行不足固定长度或者已经为空, 则直接取出剩余部分的内容放到新的数组中,继续下一行。 如样例1,获得长度3,先遍…...
不要让GPT成为你通向“学业作弊”的捷径——使用GPT检测工具来帮助你保持正确的方向
不要让GPT成为你通向“学业作弊”的捷径——使用GPT检测工具来帮助你保持正确的方向 最近,多所美国高校以及香港大学等都明确禁止在校使用ChatGPT等智能文本生成工具。GPT(Generative Pre-trained Transformer)是一种自然语言处理技术&#x…...
基于matlab的斜视模式下SAR建模
一、前言此示例说明如何使用线性 FM (LFM) 波形对基于聚光灯的合成孔径雷达 (SAR) 系统进行建模。在斜视模式下,SAR平台根据需要从宽侧斜视一定角度向前或向后看。斜视模式有助于对位于当前雷达平台位置前面的区域进行…...
15-基础加强-1-类加载器反射
文章目录1.类加载器1.1类加载器【理解】1.2类加载的过程【理解】1.3类加载的分类【理解】1.4双亲委派模型【理解】1.5ClassLoader 中的两个方法【应用】2.反射2.1反射的概述【理解】2.2获取Class类对象的三种方式【应用】 第1步:获取类的Class对象2.3反射获取构造方…...
基于SSM,Spring, BootStrap 毕业设计管理系统的设计与实现
目录 一.前言介绍 二、主要技术 2.1 SSM框架介绍 2.2 MYSQL数据库 2.3 持久层框架MyBatis 2.4 前端框架BootStrap 三. 系统设计 3.1 系统架构设计 3.2 系统功能模块 3.2.1 学生模块 3.2.2 教师模块 3.2.3 管理员模块 四、数据库设计 4.1 数据分析 4.2 概念设计 …...
一招鉴别真假ChatGPT,并简要介绍ChatGPT、GPT、GPT2和GPT3模型之间的区别和联系
以下内容除红色字体部分之外,其他均来源于ChatGPT自动撰写。 ChatGPT是基于GPT模型的对话生成模型,旨在通过对话模拟实现自然语言交互。它是为了改善人机对话体验而设计的,主要应用于聊天机器人、智能客服等场景。 与GPT模型相比,…...
华为OD机试 - 特异性双端队列(JS)
特异性双端队列 题目 有一个特异性的双端队列,该队列可以从头部到尾部添加数据,但是只能从头部移除数据。 小A一次执行 2n 个指令往队列中添加数据和移除数据, 其中 n 个指令是添加数据(可能从头部也可以从尾部添加) 依次添加 1 到 n , n 个指令是移出数据 现在要求移除数…...
Nginx自动封禁可疑Ip
文章目录一、Nginx封禁ip1、简介2、nignx 禁止IP访问2.1 方法一2.2 方法二3、关于 deny 的使用二、脚本自动封禁Ip1、流程介绍2、脚本实战2.1 核心脚本解释2.2 编写shell脚本2.3 crontab定时一、Nginx封禁ip 1、简介 在网站维护过程中,有时候我们需要对一些IP地址…...
分布式事务--理论基础
1、事务基础 1.1、什么是事务 事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。 1.2、本地事务 在同一个进程内,控制同一数据源的事务,称为本地事务。例如数据库事务。 在计…...
Matlab数学建模常用算法及论文插图绘制模板资源合集
最近有很多朋友咨询我关于Matlab论文插图绘制方面的问题。 问了一下,这些朋友中,除了写博士论文的,大部分都是要参加美赛的。 这让我突然想起,自己曾经为了水论文,购买过一批Matlab数学建模的资料。 想了想…...
C语言【动态内存管理 后篇】
动态内存管理 后篇🫅经典例题🤦♂️题目1🤦♂️题目2🤦♂️题目3🤦♂️题目4🫅C/C程序的内存开辟前面的一篇文章动态内存管理 前篇,我们已经了解过了动态内存管理的相关信息,…...
四大步骤,教你彻底关闭Win10自动更新
文章目录一、禁用Windows Update服务二、在组策略里关闭Win10自动更新相关服务三、禁用任务计划里边的Win10自动更新四、在注册表中关闭Win10自动更新参考资料一、禁用Windows Update服务 1、同时按下键盘 Win R,打开运行对话框,然后输入命令 services…...
通信算法之一百零四:QPSK完整收发仿真链路
1.发射机物理层基带仿真链路 1.1 % Generates the data to be transmitted [transmittedBin, ~] BitGenerator(); 2.2 % Modulates the bits into QPSK symbols modulatedData QPSKModulator(transmittedBin); 2.3 % Square root Raised Cosine Transmit Filter %comm…...
时间复杂度(超详解+例题)
全文目录引言如何衡量一个算法的好坏时间复杂度时间复杂度的定义时间复杂度的大O表示法实例test1test2test3test4test5总结引言 如何衡量一个算法的好坏 我们在写算法的时候,对于实现同样的作用的不同算法,我们如何判断这个算法的好坏呢? …...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
