我一直在尝试在 rust 中实现策略模式,但我无法理解如何使其工作。
所以让我们假设我们有一个 trait Adder 和 Element:
pub trait Element {
fn to_string(&self) -> String;
}
pub trait Adder {
type E: Element;
fn add (&self, a: &Self::E, b: &Self::E) -> Self::E;
}
我们有两个实现 StringAdder 和 StringElements 和 UsizeAdder 和 UsizeElements:
// usize
pub struct UsizeElement {
pub value: usize
}
impl Element for UsizeElement {
fn to_string(&self) -> String {
self.value.to_string()
}
}
pub struct UsizeAdder {
}
impl Adder for UsizeAdder{
type E = UsizeElement;
fn add(&self, a: &UsizeElement, b: &UsizeElement) -> UsizeElement{
UsizeElement { value: a.value + b.value }
}
}
// String
pub struct StringElement {
pub value: String
}
impl Element for StringElement {
fn to_string(&self) -> String {
self.value.to_string()
}
}
pub struct StringAdder {
}
impl Adder for StringAdder {
type E = StringElement;
fn add(&self, a: &StringElement, b: &StringElement) -> StringElement {
let a: usize = a.value.parse().unwrap();
let b: usize = b.value.parse().unwrap();
StringElement {
value: (a + b).to_string()
}
}
}
而且我想编写一个使用 Adder trait 的 trait 方法的代码,它是相应的元素,而不知道在编译时将使用哪种策略。
fn main() {
let policy = "usize";
let element = "1";
let adder = get_adder(&policy);
let element_a = get_element(&policy, element);
let result = adder.add(element_a, element_a);
}
为简化起见,我将为策略和元素分配一个字符串,但通常会从文件中读取。
是使用动态调度实现 get_adder 和 get_element 的唯一方法吗?通过扩展,我应该定义 Adder 和 Element 特征以使用特征对象和/或 Any 特征吗?
编辑:这是我迄今为止设法弄清楚的。
一个可能的实现示例是使用 match 来帮助定义编译器的具体类型。
fn main() {
let policy = "string";
let element = "1";
let secret_key = "5";
let result = cesar(policy, element, secret_key);
dbg!(result.to_string());
}
fn cesar(policy: &str, element: &str, secret_key: &str) -> Box<dyn Element>{
match policy {
"usize" => {
let adder = UsizeAdder{};
let element = UsizeElement{ value: element.parse().unwrap() };
let secret_key = UsizeElement{ value: secret_key.parse().unwrap() };
Box::new(cesar_impl(&adder, &element, &secret_key))
}
"string" => {
let adder = StringAdder{};
let element = StringElement{ value: element.to_string() };
let secret_key = StringElement{ value: secret_key.to_string() };
Box::new(cesar_impl(&adder, &element, &secret_key))
}
_ => {
panic!("Policy not supported!")
}
}
}
fn cesar_impl<A>(adder: &A, element: &A::E, secret_key: &A::E) -> A::E where A: Adder, A::E : Element {
adder.add(&element, &secret_key)
}
然而问题是我必须使用匹配函数来包装我想要实现的每个函数来确定具体类型,以及每个可用策略的案例。
它似乎不是实现它的正确方法,因为它会使代码膨胀,使其更容易出错且更不易维护,除非我最终使用宏。
编辑 2:在这里您可以找到使用动态调度的示例。但是我不相信这是实施解决方案的正确方法。
谢谢您的帮助 :)