Rust 结构模式,在 hashmap 中存储多个工厂

Rust fabric pattern, storing multiple factories in hashmap

我希望能够将多个工厂存储在一个 hashmap 中,以便稍后将它们添加到其中(例如通过插件),然后在应用程序中通过键名获取每个工厂(这是一个资源管理器) .

问题出在 Fabric 特性的泛型上,Fabric 可以创造不同类型的水果,但我需要在这里指定一些东西 HashMap<String, Box<dyn Fabric>>,例如 HashMap<String, Box<Fabric<Apple>>>HashMap<String, Box<Fabric<T>>> 这是也不是很有用,因为正如我所说,我们可以创造出真正不同的水果。

另外我猜 foo 方法可能有问题,关于借用内容。

那么你将如何实现这个 "the rust way"?

use std::collections::HashMap;

trait Fruit {
    fn get_name(&self) -> String;
}

trait Fabric<T: Fruit> {
    fn build(&self) -> Box<T>;
}

struct Banana {}
impl Fruit for Banana {
    fn get_name(&self) -> String { String::from("I'm banana") }
}

struct BananaFabric {}
impl Fabric<Banana> for BananaFabric  {
    fn build(&self) -> Box<Banana> {
        Box::new(Banana {})
    }
}

struct Apple {}
impl Fruit for Apple {
    fn get_name(&self) -> String { String::from("I'm apple") }
}

struct AppleFabric {}
impl Fabric<Apple> for AppleFabric  {
    fn build(&self) -> Box<Apple> {
        Box::new(Apple {})
    }
}

struct C {
    map: HashMap<String, Box<dyn Fabric>>,
}

impl C {
    pub fn new() -> C {
        C {
            map: HashMap::new()
        }
    }

    pub fn foo(&self, key: String) {
        match self.map.get(&key) {
            Some(&fabric) => {
                let fruit = fabric.build();
                println!("{}", fruit.get_name())
            },
            _ => println!("No fabric found")
        }
    }
}

fn main() {
    let c = C::new();
    c.foo(String::from("bar"));
}

你的意思是像

use std::collections::HashMap;

trait A {
    fn boo(&self) -> i32;
}

struct B {}

impl A for B {
    fn boo(&self) -> i32 {
        15
    }
}

struct C {
    map: HashMap<String, Box<dyn A>>,
}

impl C {
    pub fn new() -> C {
        C {
            map: HashMap::new(),
        }
    }

    pub fn foo(&self, key: String) {
        match self.map.get(&key) {
            Some(val) => println!("{}", val.boo()),
            _ => println!("None"),
        }
    }
}
fn main() {
    let mut c = C::new();

    c.map.insert(String::from("bar"), Box::new(B{}));

    c.foo(String::from("bar"));
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f9d6d9ff94eb8714ef6a6d0ab023fe84

顺便说一句。这是 Factory Design Pattern.

我能想到两个方案:

动态调度(特征对象):

trait Fabric {
  fn build(&self) -> Box<dyn Fruit>;
}

[...]

impl Fabric for BananaFabric  {
  fn build(&self) -> Box<dyn Fruit> {
    Box::new(Banana {})
  }
}

使用 enum:

enum Fruits {
  Banana, 
  Apple
}
impl Fruit for Fruits {
  fn get_name(&self) -> String { 
    match self {
      Banana => String::from("I'm banana"),
      Apple => String::from("I'm apple"),
      _ => String::from("")
    }
  }
}

[...]

impl Fabric for BananaFabric  {
  fn build(&self) -> Box<Fruits> {
    Box::new(Fruits::Banana)
  }
}

在这两种情况下,foo 方法将如下所示:

pub fn foo(&self, key: String) {
  match self.map.get(&key) {
    Some(fabric) => {
      let fruit = fabric.build();
        println!("{}", fruit.get_name())
      },
      _ => println!("No fabric found")
  }
}