Rust构建器模式是否必须使用冗余结构代码? [英] Do Rust builder patterns have to use redundant struct code?
问题描述
我正在查看Rust文档的方法语法部分,并看到了一个构建器模式的示例。下例中的 CircleBuilder
结构与 Circle
结构完全相同。这种冗余代码似乎违反了通常的编程规范。
I was looking at the Method syntax section of the Rust documentation and came across an example of the builder pattern. The CircleBuilder
struct in the example below is an exact duplicate of the Circle
struct. It seems like this redundant code violates the usual norms of programming.
我理解该示例为何创建新结构,因为创建者不想针对原始 Circle $ c来实现builder方法。 $ c>结构。很好,但是有没有办法重写此示例,以确保没有冗余-仍然保持
main()
函数中漂亮的生成器接口完整?
I understand why the example created a new struct, because the creator did not want to implement the builder methods against the original Circle
struct. That is fine, but is there a way to rewrite this example so that there is no redundancy--yet still keeping the nice builder interface in the main()
function intact?
我试图创建一个空结构或仅包含一个一次性元素的结构,但这没有用。
I tried to create an empty struct or a struct with just one throwaway element, but that did not work.
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
struct CircleBuilder {
x: f64,
y: f64,
radius: f64,
}
impl CircleBuilder {
fn new() -> CircleBuilder {
CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
}
fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.x = coordinate;
self
}
fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.y = coordinate;
self
}
fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
self.radius = radius;
self
}
fn finalize(&self) -> Circle {
Circle { x: self.x, y: self.y, radius: self.radius }
}
}
fn main() {
let c = CircleBuilder::new()
.x(1.0)
.y(2.0)
.radius(2.0)
.finalize();
println!("area: {}", c.area());
println!("x: {}", c.x);
println!("y: {}", c.y);
}
推荐答案
Rust构建器模式是否必须使用冗余结构代码?
Do Rust builder patterns have to use redundant struct code?
否。但是有时候他们可能。例如,考虑是否要对构造函数使用特殊的逻辑(甚至只是复杂的逻辑):
No. But sometimes they might. For example, consider if we wanted to have special logic (or even just complicated logic) around our constructor:
/// Width must always be greater than height!
struct HorizontalEllipse {
width: f64,
height: f64,
}
impl HorizontalEllipse {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.width / 2.0) * (self.height / 2.0)
}
}
struct HorizontalEllipseBuilder {
width: f64,
height: f64,
}
impl HorizontalEllipseBuilder {
fn new() -> HorizontalEllipseBuilder {
HorizontalEllipseBuilder {
width: 0.0,
height: 0.0,
}
}
fn width(&mut self, width: f64) -> &mut HorizontalEllipseBuilder {
self.width = width;
self
}
fn height(&mut self, height: f64) -> &mut HorizontalEllipseBuilder {
self.height = height;
self
}
fn finalize(&self) -> Result<HorizontalEllipse, String> {
let HorizontalEllipseBuilder { height, width } = *self;
if height >= width {
Err("This is not horizontal".into())
} else {
Ok(HorizontalEllipse { width, height })
}
}
}
fn main() {
let c = HorizontalEllipseBuilder::new()
.width(1.0)
.height(2.0)
.finalize()
.expect("not a valid ellipse");
println!("area: {}", c.area());
println!("width: {}", c.width);
println!("height: {}", c.height);
}
现在有 HorizontalEllipse
知道 width>高度
。我们已将该检查从许多潜在位置(每个方法)移至构造函数一个位置。然后我们将构造函数移到新的类型,因为它很复杂(不是真的,但真正复杂的示例通常...很复杂)。
Now a HorizontalEllipse
knows that it is always true that width > height
. We've moved that check from many potential places (each method) to one, the constructor. We then moved the constructor to a new type because it was complicated (not really, but truly complicated examples are usually... complicated).
我见过很多构建器还具有增强类型的真实对象:
Many builders I've seen also have "enhanced" types of the real object:
#[derive(Debug)]
struct Person {
name: String,
}
#[derive(Debug, Default)]
struct PersonBuilder {
name: Option<String>,
}
impl PersonBuilder {
fn name(self, name: &str) -> Self {
PersonBuilder { name: Some(name.into()), ..self }
}
fn build(self) -> Person {
Person {
name: self.name.unwrap_or_else(|| "Stefani Joanne Angelina Germanotta".into()),
}
}
}
fn main() {
let person = PersonBuilder::default().build();
println!("{:?}", person);
let person = PersonBuilder::default().name("krishnab").build();
println!("{:?}", person);
}
在本书的示例中您没有看到它,因为它试图变得更简单而不涉及所有权问题。
You don't see that in the book's example because it's trying to be simpler and not involve ownership concerns.
这篇关于Rust构建器模式是否必须使用冗余结构代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!