如何在 Rust 中实现抽象工厂?

use*_*449 3 rust

就像是:

trait Thingy {
    fn hallo(&self);
}

trait Factory {
    fn make(&self) -> Thingy;
}
//------------------------------
struct AThingy {}

impl Thingy for AThingy {
    fn hallo(&self) {
        println!("i'm A thingy");
    }
}

struct AFactory {}

impl Factory for AFactory {
    fn make(&self) -> AThingy {
        AThingy{}
    }
}

//------------------------------
struct BThingy {}

impl Thingy for BThingy {
    fn hallo(&self) {
        println!("i'm B thingy");
    }
}

struct BFactory {}

impl Factory for BFactory {
    fn make(&self) -> BThingy {
        BThingy{}
    }
}

//------------------------------
#[test]
fn test_factory() {
    let aFactory:Factory = AFactory{};
    let bFactory:Factory = BFactory{};
    
    aFactory.make().hallo();
    bFactory.make().hallo();
}
Run Code Online (Sandbox Code Playgroud)

试图在不同的地方附加 Sized 但都失败了。

Pet*_*all 7

什么时候Thingy是一个特质,这个:

fn make(&self) -> Thingy;
Run Code Online (Sandbox Code Playgroud)

相当于:

fn make(&self) -> dyn Thingy;
Run Code Online (Sandbox Code Playgroud)

也就是说,一个未调整大小的裸特征对象。你不能像 Rust 那样处理未定义大小的类型;特征对象需要位于某种类型的指针后面。鉴于该函数创建对象,它不能是 a &dyn Thingy,因此您需要一个拥有的指针,例如Box<dyn Thingy>

所以你的Factory特质看起来像这样:

trait Factory {
    fn make(&self) -> Box<dyn Thingy>;
}
Run Code Online (Sandbox Code Playgroud)

实现如下所示:

impl Factory for AFactory {
    fn make(&self) -> Box<dyn Thingy> {
        Box::from(AThingy{})
    }
}
Run Code Online (Sandbox Code Playgroud)

鉴于您的问题,我猜您正在尝试应用您习惯的面向对象语言(例如 Java)的模式。这些模式违背了 Rust 中常用的习惯用法,并迫使您对数据进行装箱,否则这些数据可能不需要装箱。在 Java 中,几乎所有通用的东西都被装箱了,并且你为此付出了代价。

  • @user656449 工厂仍然是有效的 Rust 模式,但它们通常会被建模为具有关联“输出”的特征,正如我在回答中所解释的那样。 (2认同)

Ibr*_*med 5

您可以使用关联类型Factory可以有一个关联的类型,称为Output. 您可以添加需要Output实现的边界Thingy

trait Factory {
    type Output: Thingy;
    
    fn make(&self) -> Self::Output;
}
Run Code Online (Sandbox Code Playgroud)

现在AFactoryOutput将是AThingy

impl Factory for AFactory {
    type Output = AThingy;
    
    fn make(&self) -> AThingy {
        AThingy {}
    }
}
Run Code Online (Sandbox Code Playgroud)

BFactoryOutput将是BThingy

impl Factory for BFactory {
    type Output = BThingy;
    
    fn make(&self) -> BThingy {
        BThingy {}
    }
}
Run Code Online (Sandbox Code Playgroud)

正如@PeterHall 提到的,您无法在 Rust 中处理未定义大小的类型,因此要存储 aFactory您需要使用拥有的指针,例如Box<dyn Factory>

#[test]
fn test_factory() {
    let aFactory: Box<dyn Factory> = Box::new(AFactory {});
    let bFactory: Box<dyn Factory> = Box::new(BFactory {});

    aFactory.make().hallo();
    bFactory.make().hallo();
}
Run Code Online (Sandbox Code Playgroud)

但是,因为Factory有关联类型,所以Output在将其放入特征对象时还必须指定:

#[test]
fn test_factory() {
    let aFactory: Box<dyn Factory<Output = AThingy>> = AFactory {};
    let bFactory: Box<dyn Factory<Output = BThingy>> = BFactory {};

    aFactory.make().hallo();
    bFactory.make().hallo();
}
Run Code Online (Sandbox Code Playgroud)