访问者模式是为一组类添加一个操作,而不修改每个类本身。
Rust中实现访问者模式,假设我们有两种具体的数据类型:X和Y,为简单起见,我们假设这些类只是没有方法的纯结构体,因此称之为Data。同时我们定义Action Trait表示我们想要应用于Data上的操作。
下面的代码是实现访问者模式的最小样板代码:
trait Data {
fn call<A: Action>(&self, action: &A) -> A::Result;
}
trait Action {
type Result;
fn act_x(&self, x: &X) -> Self::Result;
fn act_y(&self, y: &Y) -> Self::Result;
}
struct X {
// ...
}
impl Data for X {
fn call<A: Action>(&self, action: &A) -> A::Result {
action.act_x(self)
}
}
struct Y {
// ...
}
impl Data for Y {
fn call<A: Action>(&self, action: &A) -> A::Result {
action.act_y(self)
}
}
为了添加一个动作,我们创建了实现action的结构体:
struct Action1;
impl Action for Action1 {
type Result = i32;
fn act_x(&self, x: &X) -> Self::Result { 1 }
fn act_y(&self, x: &Y) -> Self::Result { 2 }
}
struct Action2;
impl Action for Action2 {
type Result = &'static str;
fn act_x(&self, x: &X) -> Self::Result { "x" }
fn act_y(&self, x: &Y) -> Self::Result { "y" }
}
现在我们有了Data = {X, Y}和Action = {Action1, Action2},我们就可以得到所有可能的组合。
fn main() {
let x = X{ /* ... */ };
let y = Y{ /* ... */ };
let a1 = Action1;
let a2 = Action2;
// {X, Y} x {Action1, Action2}
let result_x1 = x.call(&a1);
let result_x2 = x.call(&a2);
let result_y1 = y.call(&a1);
let result_y2 = y.call(&a2);
}
通过这种方式,我们可以在不修改类本身的情况下向现有的类添加任意多个操作。缺点是它使代码冗长。访问者模式的替代方案是什么?
对于Rust,我们可以使用enum模式匹配:
enum Data {
X(X),
Y(Y),
}
struct X {
// fill in
}
struct Y {
// fill in
}
fn action1(data: &Data) -> i32 {
match data {
Data::X(x) => 1,
Data::Y(y) => 2,
}
}
fn action2(data: &Data) -> &'static str {
match data {
Data::X(x) => "x",
Data::Y(y) => "y",
}
}
fn perform_all_combinations() {
let x = Data::X(X{ /* ... */ });
let y = Data::Y(Y{ /* ... */ });
let x1 = action1(&x);
let x2 = action2(&x);
let y1 = action1(&y);
let y2 = action2(&y);
}
这极大地简化了代码,这是否意味着我们应该总是使用枚举模式匹配而不是访问者模式呢?
通常情况下,天下没有免费的午餐。枚举模式匹配的一个缺点是,通过创建枚举,Data的对象大小将被设置为其变体中最大的,因此可能会浪费内存空间。可以通过将X和Y包装到Box中来缓解这个问题,但这本身又引入了另一种间接方式,为动态内存分配带来了额外的开销。
然而,访问者模式的典型用例可能需要在运行时动态确定数据类型,因此访问者模式也可能需要Box开销。
总之,这两种模式让我们实现了同样的事情,我认为没有哪一种模式在所有方面都比另一种更好。更确切地说,这可能取决于个人对代码组织方式的偏好。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8