使用 Builder 模式时,我应该通过值还是可变引用来获取“self”?
Posted
技术标签:
【中文标题】使用 Builder 模式时,我应该通过值还是可变引用来获取“self”?【英文标题】:Should I take `self` by value or mutable reference when using the Builder pattern? 【发布时间】:2022-01-21 06:37:03 【问题描述】:到目前为止,我已经在官方 Rust 代码和其他 crate 中看到了两种构建器模式:
impl DataBuilder
pub fn new() -> DataBuilder ...
pub fn arg1(&mut self, arg1: Arg1Type) -> &mut Builder ...
pub fn arg2(&mut self, arg2: Arg2Type) -> &mut Builder ...
...
pub fn build(&self) -> Data ...
impl DataBuilder
pub fn new() -> DataBuilder ...
pub fn arg1(self, arg1: Arg1Type) -> Builder ...
pub fn arg2(self, arg2: Arg2Type) -> Builder ...
...
pub fn build(self) -> Data ...
我正在编写一个新的 crate,但我有点困惑我应该选择哪种模式。我知道如果我以后更改一些 API 会很痛苦,所以我想现在就做出决定。
我理解它们之间的语义差异,但在实际情况下我们应该更喜欢哪一个?或者我们应该如何选择它们?为什么?
【问题讨论】:
FWIW,derive_builder
板条箱列出了一些优点和缺点:docs.rs/derive_builder/latest/derive_builder/#builder-patterns。
【参考方案1】:
从同一个构建器构建多个值是否有益?
如果是,请使用&mut self
如果没有,请使用self
考虑std::thread::Builder
,它是std::thread::Thread
的构建器。它在内部使用Option
字段来配置如何构建线程:
pub struct Builder
name: Option<String>,
stack_size: Option<usize>,
它使用self
到.spawn()
线程,因为它需要name
的所有权。它理论上可以使用&mut self
和.take()
字段外的名称,但随后对.spawn()
的调用不会产生相同的结果,这是一个糟糕的设计。它可以选择.clone()
名称,但是生成线程会产生额外且通常不需要的成本。使用 &mut self
会造成不利影响。
考虑std::process::Command
,它充当std::process::Child
的构建器。它具有包含程序、参数、环境和管道配置的字段:
pub struct Command
program: CString,
args: Vec<CString>,
env: CommandEnv,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
// ...
它使用&mut self
到.spawn()
,因为它不拥有这些字段的所有权来创建Child
。无论如何,它必须在内部将所有数据复制到操作系统,因此没有理由使用self
。生成具有相同配置的多个子进程还有一个切实的好处和用例。
考虑std::fs::OpenOptions
,它充当std::fs::File
的构建器。它只存储基本配置:
pub struct OpenOptions
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
// ...
它使用&mut self
到.open()
,因为它不需要拥有任何东西才能工作。它有点类似于线程构建器,因为有一个与文件关联的路径,就像有一个与线程关联的名称一样,但是,文件路径仅传递给.open()
,而不是与构建器一起存储。有一个用例可以打开具有相同配置的多个文件。
上述注意事项实际上只涵盖了.build()
方法中self
的语义,但有充分的理由表明,如果您选择一种方法,您也应该将其用于临时方法:
(&mut self) -> &mut Self
链接到build(self)
显然不会编译
将(self) -> Self
用于build(&mut self)
会限制构建器长期重用的灵活性
【讨论】:
以上是关于使用 Builder 模式时,我应该通过值还是可变引用来获取“self”?的主要内容,如果未能解决你的问题,请参考以下文章
需要多个参数输入时-----------------考虑使用变种的Builder模式