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

kri*_*nab 5 struct builder rust

我正在查看Rust 文档的方法语法部分,并发现了构建器模式的示例。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);
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 5

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

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

/// 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);
}
Run Code Online (Sandbox Code Playgroud)

现在aHorizontalEllipse知道这始终是真的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);
}
Run Code Online (Sandbox Code Playgroud)

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