struct - Rust 构建器模式是否必须使用冗余结构代码?

标签 struct rust builder

我正在查看 Method syntax section of the Rust documentation并遇到了构建器模式的示例。下面示例中的 CircleBuilder 结构与 Circle 结构完全相同。这段冗余代码似乎违反了通常的编程规范。

我明白为什么这个例子创建了一个新的结构,因为创建者不想针对原始的 Circle 结构实现构建器方法。这很好,但是有没有一种方法可以重写此示例以便没有冗余——同时仍然保持 main() 函数中漂亮的构建器界面完好无损?

我试图创建一个空结构或仅包含一个一次性元素的结构,但这没有用。

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);
}

最佳答案

Do Rust builder patterns have to use redundant struct code?

没有。但有时他们可能。例如,考虑一下我们是否希望在我们的构造函数周围有特殊的逻辑(或者甚至只是复杂的逻辑):

/// 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 > height 始终为真。我们已将该检查从许多可能的地方(每个方法)移到一个地方,即构造函数。然后我们将构造函数移动到一个新类型,因为它很复杂(不是真的,但真正复杂的例子通常……很复杂)。

我见过的许多构建器还具有真实对象的“增强”类型:

#[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);
}

您在本书的示例中看不到这一点,因为它试图变得更简单并且不涉及所有权问题。

关于struct - Rust 构建器模式是否必须使用冗余结构代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43840415/

相关文章:

string - 交替使用 str 和 String

java - Spliterator 与 Stream.Builder

c++ - 我们可以在 C++ 的 main 函数之后定义结构吗?

rust - 在当前范围内找不到类型 `generate_keypair` 的名为 `secp256k1::Secp256k1` 的方法

c++ - 通过void指针修改结构体的内容

rust - 是否有可能在不使用 Box 的情况下返回一个返回闭包的 Rust 闭包?

ruby - 使用可重用代码创建 Ruby 构建器对象

java - Builder Factory 返回不同的子接口(interface)

C:关于struct中二维数组的初学者问题

c - 为什么在以下情况下分配后结构地址不等于