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

sqyvllje  于 2023-05-29  发布在  其他
关注(0)|答案(3)|浏览(173)

我正在查看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);
}
ve7v8dk2

ve7v8dk21#

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

现在一个HorizontalEllipse知道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);
}

你在书中的例子中看不到这一点,因为它试图更简单,不涉及所有权问题。

ivqmmu1c

ivqmmu1c2#

这似乎是一个宏可以做的事情。快速搜索找到了似乎实现了此功能的derive_builderbuilder_macro板条箱。

yyhrrdl8

yyhrrdl83#

我创建了一个名为default-struct-builder的宏crate,旨在避免生成单独的构建器结构,甚至是build()方法。
它的目的是在简单的情况下用于实现构造函数(就像您所做的那样)或Default的结构。
你可以像这样把它添加到你的结构体中:

use default_struct_builder::DefaultBuilder;

#[derive(DefaultBuilder)]
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

然后像这样简单地使用它:

let circle = Circle::new()
    .x(10.0)
    .radius(2.5);

assert_eq!(circle.x, 10.0);
assert_eq!(circle.y, 0.0);
assert_eq!(circle.radius, 2.5);

相关问题