Ржавый узор ткани, хранение нескольких фабрик в hashmap - PullRequest
1 голос
/ 20 октября 2019

Я хочу иметь возможность хранить несколько фабрик в одной хэш-карте, чтобы позже добавить их в нее (например, с помощью плагинов), а затем получить каждую из них по ключу в приложении (это менеджер ресурсов).

Проблема заключается в общем свойстве «Ткань», ткани могут создавать плоды разных типов, но мне нужно указать здесь что-то HashMap<String, Box<dyn Fabric>>, например HashMap<String, Box<Fabric<Apple>>> или HashMap<String, Box<Fabric<T>>>, что также не очень полезно, посколькуЯ сказал, что мы можем создавать действительно разные фрукты.

Также я думаю, что в методе foo может возникнуть проблема с заимствованием контента.

Так как бы вы реализовали этот «путь ржавчины»?

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

Ответы [ 2 ]

1 голос
/ 21 октября 2019

Я могу представить себе два варианта:

Динамическая отправка (объекты признаков) :

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")
  }
}
0 голосов
/ 20 октября 2019

Вы имеете в виду что-то вроде

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

BTW. это Factory Design Pattern.

...