V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaopanzi
V2EX  ›  Rust

Rust 如何在不支持继承的情况实现 getter/setter 的代码复用?

  •  
  •   xiaopanzi · 2021-05-26 11:12:16 +08:00 · 2797 次点击
    这是一个创建于 1280 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 中针对这个场景,如何能够实现代码复用?

    13 条回复    2021-05-26 15:55:09 +08:00
    jedrek
        1
    jedrek  
       2021-05-26 13:17:08 +08:00
    吃饭有吃饭的方式,不需要用吃屎的方式吃饭
    VDimos
        2
    VDimos  
       2021-05-26 13:42:56 +08:00 via Android
    写个宏呗,derive macro 也可以
    iikebug
        3
    iikebug  
       2021-05-26 13:44:14 +08:00
    1 楼说的对,不要用吃屎的方式吃饭
    Vegetable
        4
    Vegetable  
       2021-05-26 13:46:46 +08:00
    你是换了语言,不是换了一种 JAVA,不要被写法限制是想法
    xiaopanzi
        5
    xiaopanzi  
    OP
       2021-05-26 13:47:36 +08:00
    @jedrek 请大佬明示。用 Rust 模仿 Java 确实不妥。但如果要实现类似功能(比如这里的 Strategy Pattern ),Rust 的“吃饭”方式应该是什么?
    xiaopanzi
        6
    xiaopanzi  
    OP
       2021-05-26 13:48:20 +08:00
    @VDimos 谢谢提示。我学习一下 Macro 的写法。
    Jirajine
        7
    Jirajine  
       2021-05-26 13:50:43 +08:00 via Android
    rust 有 sum type,大多数情况下传统 oo 里的继承都是不必要的。
    如果是多个类型共同的行为,那就用 trait 。
    xiaopanzi
        8
    xiaopanzi  
    OP
       2021-05-26 14:05:07 +08:00
    @Jirajine I see 。我能理解只有 trait 而不用继承的 trade-off 。那么具体到对于“试图共享 get/set 的具体实现”这一基本问题,在 Rust 中是不是除了重复就别无他法(不引入 macro 的情况下)?另外,Rust 中针对 Strategy Pattern 是否有更好的解决方案,而不用写 get/set ?再进一步,鉴于 Rust 不是传统的 OO 语言,是不是很多时候写 Design Pattern 就注定了是“吃屎”的写法?
    Jirajine
        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,放宽思路,回过头来就豁然开朗了。
    Jirajine
        10
    Jirajine  
       2021-05-26 14:51:59 +08:00
    @xiaopanzi #8 总的来说,一般使用继承的场景,表达 sum type -> 用 enum,表达 subtyping -> 用泛型,表达接口 ->用 trait 。
    jedrek
        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() { }
    }
    ```
    xiaopanzi
        12
    xiaopanzi  
    OP
       2021-05-26 15:54:57 +08:00
    @Jirajine 学习了。多谢!
    xiaopanzi
        13
    xiaopanzi  
    OP
       2021-05-26 15:55:09 +08:00
    @jedrek Thanks 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3379 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:30 · PVG 19:30 · LAX 03:30 · JFK 06:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.