Rust构建器模式是否必须使用冗余结构代码? [英] Do Rust builder patterns have to use redundant struct code?

查看:107
本文介绍了Rust构建器模式是否必须使用冗余结构代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在查看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 结构。很好,但是有没有办法重写此示例,以确保没有冗余-仍然保持 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);
}

现在有 Horizo​​ntalEllipse 知道 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆