Rust 建造者模式
在DDD中,DTO(数据传输对象)->BO(业务对象)、BO(业务对象)->PO(持久化对象,有的叫DO,即和数据表映射的实体)等等情况要做转换,这里提供以下转换方式
1、from或者try_from trait实现对象转换
需要转换对象满足接收对象的所有字段
客户定义
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Customer {// uuidpub user_id: String,// 用户名pub username: String,// 邮件pub email: String,// 密码pub password: String,// 头像pub avatar: Option<String>,// 验证码pub verify_code: Option<String>,// 收货地址pub receive_address: Vec<ReceiveAddress>,
}
通过实现from trait,可以从Model转换为Customer
impl From<Model> for Customer {fn from(user: Model) -> Self {Customer {user_id: user.user_id,username: user.username,email: user.email,password: user.password,avatar: user.avatar,verify_code: user.verify_code,receive_address: user.receive_address,}}
}
实现了From trait默认自动实现Into trait,你可以通过以下两种方式实现对象转换,Try from trait用法一样,只是在转换失败时可以返回错误
// 使用from方法将Model实例转换为Customer实例
let customer_instance = Customer::from(model_instance);// 或者使用更简洁的into方法,它会自动调用对应的from方法
let another_customer_instance = model_instance.into();
但是这样不够优雅,很多时候DTO并不能满足领域对象的所有字段,数据对象也不能满足领域对象的所有字段,例如以上例子的验证码和收货地址,最初没有数据时需要设置默认值
// 转Bo
impl From<Model> for Customer {fn from(user: Model) -> Self {Customer {user_id: user.user_id,username: user.username,email: user.email,password: user.password,avatar: user.avatar,verify_code: None,receive_address: vec![],}}
}
当下一次从数据库中查到数据需要给收货地址赋值的情况下,这种方案就不适用了,可以使用以下建造者模式
2、链式调用
此时所有字段都是private的,通过builder去赋值
// 注意使用了Default,没有builder的值有默认值
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Customer {// uuidpub user_id: String,// 用户名pub username: String,// 邮件pub email: String,// 密码pub password: String,// 头像pub avatar: Option<String>,// 验证码pub verify_code: Option<String>,// 收货地址pub receive_address: Vec<ReceiveAddress>,
}
impl Customer {// new默认值pub fn new() -> Self {Self {user_id: String::new(),username: String::new(),email: String::new(),password: String::new(),avatar: None,verify_code: None,receive_address: Vec::new(),}}pub fn user_id(mut self, user_id: String) -> Self {self.user_id = user_id;self}pub fn username(mut self, username: String) -> Self {self.username = username;self}pub fn email(mut self, email: String) -> Self {self.email = email;self}pub fn password(mut self, password: String) -> Self {self.password = password;self}pub fn avatar(mut self, avatar: Option<String>) -> Self {self.avatar = avatar;self}pub fn verify_code(mut self, verify_code: Option<String>) -> Self {self.verify_code = verify_code;self}pub fn receive_address(mut self, receive_address: Vec<ReceiveAddress>) -> Self {self.receive_address = receive_address;self}
}
使用
let customer = Customer::new().user_id("123".to_string()).username("张三".to_string()).email("<EMAIL>".to_string());let customer = customer.avatar(Some("https://www.baidu.com".to_string()));print!("{:?}\n", customer);//Customer { user_id: "123", username: "张三", email: "<EMAIL>", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }// 修改原有对象let customer = customer.email("123@qq.com".to_string());println!("{:?}", customer);//Customer { user_id: "123", username: "张三", email: "123@qq.com", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }
这种方式容易造成意外修改的传播,不推荐
3、建造者模式实现对象转换
在Java中很简单,加上@Builder注解即可
@Builder
public class User {private UserLastname lastname;private UserFirstname firstname;private UserEmail email;private UserPublicId userPublicId;private UserImageUrl imageUrl;private Instant lastModifiedDate;private Instant createdDate;private Set<Authority> authorities;private Long dbId;private UserAddress userAddress;private Instant lastSeen;public User(UserLastname lastname, UserFirstname firstname, UserEmail email, UserPublicId userPublicId, UserImageUrl imageUrl, Instant lastModifiedDate, Instant createdDate, Set<Authority> authorities, Long dbId, UserAddress userAddress, Instant lastSeen) {this.lastname = lastname;this.firstname = firstname;this.email = email;this.userPublicId = userPublicId;this.imageUrl = imageUrl;this.lastModifiedDate = lastModifiedDate;this.createdDate = createdDate;this.authorities = authorities;this.dbId = dbId;this.userAddress = userAddress;this.lastSeen = lastSeen;}
}
通过builder()使用,通过结尾的build()返回新对象
UserBuilder.email(user.getEmail().value()).firstName(user.getFirstname().value()).lastName(user.getLastname().value()).publicId(user.getUserPublicId().value()).authorities(RestAuthority.fromSet(user.getAuthorities())).build()
Rust实现(值传递)建造者模式
和直接链式调用相比,添加了一个build函数返回新对象
// 注意使用了Default,没有builder的值有默认值
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Customer {// uuiduser_id: String,// 用户名username: String,// 邮件email: String,// 密码password: String,// 头像avatar: Option<String>,// 验证码verify_code: Option<String>,// 收货地址receive_address: Vec<ReceiveAddress>,
}
// 建造(者结构体,包含一个需要构建的对象
#[derive(Default, Clone, Debug)]
pub struct CustomerBuilder {customer: Customer,
}
impl CustomerBuilder {pub fn new() -> Self {// 初始化默认值CustomerBuilder::default()}pub fn user_id(mut self, user_id: String) -> Self {self.customer.user_id = user_id;self}pub fn username(mut self, username: String) -> Self {self.customer.username = username;self}pub fn email(mut self, email: String) -> Self {self.customer.email = email;self}pub fn password(mut self, password: String) -> Self {self.customer.password = password;self}pub fn avatar(mut self, avatar: Option<String>) -> Self {self.customer.avatar = avatar;self}pub fn verify_code(mut self, verify_code: Option<String>) -> Self {self.customer.verify_code = verify_code;self}pub fn receive_address(mut self, receive_address: Vec<ReceiveAddress>) -> Self {self.customer.receive_address = receive_address;self}pub fn build(self) -> Customer {Customer {user_id: self.customer.user_id,username: self.customer.username,email: self.customer.email,password: self.customer.password,avatar: self.customer.avatar,verify_code: self.customer.verify_code,receive_address: self.customer.receive_address,}}
}
使用,没有建造的字段由于Default宏的存在会初始化默认值,这种用法和第二种链式调用方式相比每次创建新对象,对象无法修改,只能创建新对象,使用对象会消耗对象,适合创建值对象、响应DTO、Event(因为这些对象用完就会被Drop,创建后就不可变)
let customer_builder = CustomerBuilder::new();let customer = customer_builder.clone().user_id("123".to_string()).username("张三".to_string()).email("<EMAIL>".to_string());let customer = customer.clone().avatar(Some("https://www.baidu.com".to_string()));let customer = customer.clone().build();print!("{:?}\n", customer);// Customer { user_id: "123", username: "张三", email: "<EMAIL>", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }// 创建新对象let customer = customer_builder.clone().email("123@qq.com".to_string()).build();println!("{:?}", customer);// Customer { user_id: "", username: "", email: "123@qq.com", password: "", avatar: None, verify_code: None, receive_address: [] }
Rust实现(引用修改)建造者模式
如果不想消耗对象,可以将其字段都设置为&mut,使用clone()是为了返回的新对象是完全独立的副本
// 注意使用了Default,没有builder的值有默认值
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Customer {// uuiduser_id: String,// 用户名username: String,// 邮件email: String,// 密码password: String,// 头像avatar: Option<String>,// 验证码verify_code: Option<String>,// 收货地址receive_address: Vec<ReceiveAddress>,
}
// 建造(者结构体,包含一个需要构建的对象
#[derive(Default, Clone, Debug)]
pub struct CustomerBuilder {customer: Customer,
}
impl CustomerBuilder {pub fn new() -> Self {CustomerBuilder::default()}pub fn user_id(&mut self, user_id: String) -> &mut Self {self.customer.user_id = user_id;self}pub fn username(&mut self, username: String) -> &mut Self {self.customer.username = username;self}pub fn email(&mut self, email: String) -> &mut Self {self.customer.email = email;self}pub fn password(&mut self, password: String) -> &mut Self {self.customer.password = password;self}pub fn avatar(&mut self, avatar: Option<String>) -> &mut Self {self.customer.avatar = avatar;self}pub fn verify_code(&mut self, verify_code: Option<String>) -> &mut Self {self.customer.verify_code = verify_code;self}pub fn receive_address(&mut self, receive_address: Vec<ReceiveAddress>) -> &mut Self {self.customer.receive_address = receive_address;self}pub fn build(&self) -> Customer {Customer {user_id: self.customer.user_id.clone(),username: self.customer.username.clone(),email: self.customer.email.clone(),password: self.customer.password.clone(),avatar: self.customer.avatar.clone(),verify_code: self.customer.verify_code.clone(),receive_address: self.customer.receive_address.clone(),}}
}
使用,这里对象创建后不会消耗对象,可以通过.build()修改并返回新对象,适合创建领域模型如聚合对象
let mut binding = CustomerBuilder::new().clone();
let customer = binding.user_id("123".to_string()).username("张三".to_string()).email("<EMAIL>".to_string());
let customer = customer.avatar(Some("https://www.baidu.com".to_string()));
let customer = customer.build();
print!("{:?}\n", customer);
//Customer { user_id: "123", username: "张三", email: "<EMAIL>", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }
// 修改原有对象
let customer = binding.email("123@qq.com".to_string()).build();
println!("{:?}", customer);
//Customer { user_id: "123", username: "张三", email: "123@qq.com", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }
相关文章:
Rust 建造者模式
在DDD中,DTO(数据传输对象)->BO(业务对象)、BO(业务对象)->PO(持久化对象,有的叫DO,即和数据表映射的实体)等等情况要做转换,这里提供以下转换方式 1、from或者try_from trait实现对象转换 需要转换对象满足接收对象的所有…...
ANN DNN CNN SNN
这些缩写代表了不同类型的人工神经网络: • ANN(Artificial Neural Network):人工神经网络,是模仿人脑神经元之间连接和交互方式的计算模型。它由节点(或称为“神经元”)组成的网络,…...
go语言进阶之并发模式
并发模式 并发模式是指在程序设计中同时处理多个任务或进程的方式,以提高效率和响应性 for select循环模式 for select循环模式通常用于处理并发操作,尤其是在需要等待多个通道时。 select的执行过程主要是以下几步 阻塞等待,直到其中一…...
Spring Cloud LoadBalancer:负载均衡的服务调用
在微服务系统中,有时候一个服务会部署多个实例,在我们调用这类实例时,如何实现负载均衡的调用呢?这时候就要用到Spring Cloud的负载均衡组件LoadBalancer了 LoadBalancer简介 LoadBalancer是Spring Cloud官方提供的负载均衡组件,通过它能使客户端在多个服务实例之间分发传…...
微信小程序之轮播图
效果图 实现 <swiper class"banner" indicator-dots"true" indicator-color"rgba(255,255,255,1)" indicator-active-color"#ff0000" autoplay"true" interval"100" circular"true"><swi…...
羲和数据集收集器1.3
为了实现所要求的功能,我们需要进一步完善代码,使其能够处理多种格式的输入文件,并生成符合要求的 JSON 格式的输出文件。具体来说,我们完善了以下内容: 增强 extract_qa_pairs_from_content 函数:使其能够识别和处理不同格式的 QA 对。 确保输出文件的格式正确:每个 Q…...
UE--IOS打包失败 AutomationTool exiting with ExitCode=9 (9)
[Remote] Executing build UATHelper: 打包 (IOS): Setting up bundled DotNet SDK UATHelper: 打包 (IOS): /Users/zyh/UE5/Builds/DESKTOP-FKKSVFQ/Y/UE/UE_5.2/Engine/Build/BatchFiles/Mac/../../../Binaries/ThirdParty/DotNet/6.0.302/mac-x64 UATHelper: 打包 (IOS)…...
第8章利用CSS制作导航菜单
8.1 水平顶部导航栏 水平菜单导航栏是应用范围最广的网站导航设计,一般位于页面顶部。它适用性强,几乎适用于所有类型的网站,且设计难度低。若导航过于普通,无法承载复杂信息结构,在内容模块较多时,则需结…...
UNIX网络编程-TCP套接字编程
概述 TCP客户端/服务器程序示例是执行如下步骤的一个回射服务器: 客户端从标准输入读入一行文本,并写给服务器。服务器从网络输入读入这行文本,并回射给客户端。客户端从网络输入读入这行回射文本,并显示在标准输出上。 TCP服务器…...
美团代付微信小程序 read.php 任意文件读取漏洞复现
0x01 产品描述: 美团代付微信小程序是美团点评旗下的一款基于微信小程序技术开发的应用程序功能,它允许用户方便快捷地请求他人为自己支付订单费用。通过微信小程序,用户可以轻松实现代付操作,无需跳转到其他应用或网页…...
centos7 node升级到node18
使用jenkins发布vue3项目提示node18安装失败 错误日志: /var/lib/jenkins/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/Node18/bin/node: /lib64/libm.so.6: version GLIBC_2.27 not found (required by /var/lib/jenkins/tools/jenkins.plugins.node…...
使用Matlab建立随机森林
综述 除了神经网络模型以外,树模型及基于树的集成学习模型是较为常用的效果较好的预测模型。我们以下构建一个随机森林模型。 随机森林是一种集成学习方法,通过构建多个决策树并结合其预测结果来提高模型的准确性和稳定性。在MATLAB中,可以…...
【江苏-无锡】《无锡市市级政务信息化建设及运行维护项目支出预算标准(试行)》 -省市费用标准解读系列10
2023年3月28日,无锡市政府投资评审中心出台《无锡市市级政务信息化建设及运行维护项目支出预算标准(试行)》,此标准是为进一步提高财政评审效率,规范评审行为,保证评审质量,节约政府投资&#x…...
2024-11-12 问AI: [AI面试题] 您将如何设计一个人工智能系统来预测电信公司的客户流失?
文心一言 设计一个人工智能系统来预测电信公司的客户流失,是一个复杂但至关重要的任务,它涉及到多个步骤和关键要素。以下是一个详细的设计方案: 一、明确需求和目标 在设计智能系统之前,需要明确系统的需求和目标,…...
【数字静态时序分析】复杂时钟树的时序约束SDC写法
以上图为例,SoC芯片上往往存在几种不同的时钟源,有pll时钟、环振时钟、外部的晶振时钟,在SoC不同的模块或者不同的运行阶段使用的时钟也往往不同,所以在使用的时候,相同的模块会出现选择不同的时钟源的情况。上图的情形…...
springboot苍穹外卖实战:五、公共字段自动填充(aop切面实现)+新增菜品功能+oss
公共字段自动填充 不足 比起瑞吉外卖中的用自定义元数据类型mybatisplus的实现,这里使用的是aop切面实现,会麻烦许多,建议升级为mp。 定义好数据库操作类型 sky-common中已经定义好,OperationType。 自定义注解 AutoFill co…...
Go 语言中,golang结合 PostgreSQL 、MySQL驱动 开启数据库事务
Go 语言中,golang结合 PostgreSQL 、MySQL驱动 开启数据库事务 PostgreSQL代码说明: MySQL代码说明: PostgreSQL 在 Go 语言中,使用 database/sql 包结合 PostgreSQL 驱动(如 github.com/lib/pq)可以方便地…...
Git核心概念
目录 版本控制 什么是版本控制 为什么要版本控制 本地版本控制系统 集中化的版本控制系统 分布式版本控制系统 认识Git Git简史 Git与其他版本管理系统的主要区别 Git的三种状态 Git使用快速入门 获取Git仓库 记录每次更新到仓库 一个好的 Git 提交消息如下&#…...
网络安全技术在能源领域的应用
摘要 随着信息技术的飞速发展,能源领域逐渐实现了数字化、网络化和智能化。然而,这也使得能源系统面临着前所未有的网络安全威胁。本文从技术的角度出发,探讨了网络安全技术在能源领域的应用,分析了能源现状面临的网络安全威胁&a…...
这些场景不适合用Selenium自动化!看看你踩过哪些坑?
Selenium是自动化测试中的一大主力工具,其强大的网页UI自动化能力,让测试人员可以轻松模拟用户操作并验证系统行为。然而,Selenium并非万能,尤其是在某些特定场景下,可能并不适合用来自动化测试。本文将介绍Selenium不…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
