Rust - 訪問者模式 vs 枚舉
訪問者模式是爲一組類添加一個操作,而不修改每個類本身。
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 開銷。
總之,這兩種模式讓我們實現了同樣的事情,我認爲沒有哪一種模式在所有方面都比另一種更好。更確切地說,這可能取決於個人對代碼組織方式的偏好。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/75qrem5U7PVsBzdmsTDjfQ