Rust 不支持继承,这对从 Java 转过来的人有点困扰。近日在读设计模式的书籍,里面有个例子是:有一个抽象的 Duck 类,它有一个属性是 FlyBehavior (是个接口)。
abstract class Duck {
private FlyBehavior flyBehavior;
public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; }
public FlyBehavior getFlyBehavior() { return this.flyBehavior; }
}
再 Rust 里面,只能这样写:
trait Duck {
fn get_fly_behavior(&self) -> &dyn FlyBehavior;
fn set_fly_behavior(&mut self, fly_behavior: Box<dyn FlyBehavior>);
}
这样的话,所有实现 Duck 的其他 struct 都必须写 一模一样 的 getter/setter 。
所以,在 Rust 中针对这个场景,如何能够实现代码复用?
1
jedrek 2021-05-26 13:17:08 +08:00
吃饭有吃饭的方式,不需要用吃屎的方式吃饭
|
2
VDimos 2021-05-26 13:42:56 +08:00 via Android
写个宏呗,derive macro 也可以
|
3
iikebug 2021-05-26 13:44:14 +08:00
1 楼说的对,不要用吃屎的方式吃饭
|
4
Vegetable 2021-05-26 13:46:46 +08:00
你是换了语言,不是换了一种 JAVA,不要被写法限制是想法
|
5
xiaopanzi OP @jedrek 请大佬明示。用 Rust 模仿 Java 确实不妥。但如果要实现类似功能(比如这里的 Strategy Pattern ),Rust 的“吃饭”方式应该是什么?
|
7
Jirajine 2021-05-26 13:50:43 +08:00 via Android
rust 有 sum type,大多数情况下传统 oo 里的继承都是不必要的。
如果是多个类型共同的行为,那就用 trait 。 |
8
xiaopanzi OP @Jirajine I see 。我能理解只有 trait 而不用继承的 trade-off 。那么具体到对于“试图共享 get/set 的具体实现”这一基本问题,在 Rust 中是不是除了重复就别无他法(不引入 macro 的情况下)?另外,Rust 中针对 Strategy Pattern 是否有更好的解决方案,而不用写 get/set ?再进一步,鉴于 Rust 不是传统的 OO 语言,是不是很多时候写 Design Pattern 就注定了是“吃屎”的写法?
|
9
Jirajine 2021-05-26 14:49:22 +08:00
@xiaopanzi #8 trait 中方法的实现当然可以共享,但你这种共享属性的用法肯定不适用。你把 Java 专用的 Design Pattern 八股文往其他语言上套,那当然是“吃屎”的写法。
因为范式不同,代码组织的方式也不同,很难直接套上。你要类似的可以看这个 https://rust-unofficial.github.io/patterns/patterns/behavioural/strategy.html 被 JavaOOP 设计模式毒害太深的话可以学一下 haskell,放宽思路,回过头来就豁然开朗了。 |
10
Jirajine 2021-05-26 14:51:59 +08:00
@xiaopanzi #8 总的来说,一般使用继承的场景,表达 sum type -> 用 enum,表达 subtyping -> 用泛型,表达接口 ->用 trait 。
|
11
jedrek 2021-05-26 15:17:13 +08:00 1
设计模式的出现,某种程度上是针对某种某类编程语言特点或弥补它的不足,若有些编程语言没有这些问题,自不必要考虑这些设计模式,硬套只会创造本不应有的问题。
getter/setter 概念是 OOP 宗教化出现的东西,本质上控制某个属性读写权限,实际使用中也是如此。Rust 的设计没有这种将值函数化的意图, 如果你仅仅想使用 Rust 实现策略模式,可以这样写 ```rust fn main() { Context { strategy: StrategyA }.execute(); Context { strategy: StrategyB }.execute(); } struct Context<T: Strategy> { strategy: T } impl<T: Strategy> Context<T> { fn execute(&self) {} } trait Strategy { fn do_something(); } struct StrategyA; struct StrategyB; impl Strategy for StrategyA { fn do_something() { } } impl Strategy for StrategyB { fn do_something() { } } ``` |