Rust(十七)-面向对象编程特性

563 阅读4分钟

Rust是面向对象编程语言吗?

  • rust受到多种编程范式的影响,包括面向对象
  • 面向对象通常包含以下特性:命名对象、封装、继承
  • 对象包含数据和形为
    • 面对对象的程序是由对象组成
    • 对象包装了数据和操作这些数据的过程,这些过程通常被称作方法或操作
  • 基于此定义:Rust是面向对象的
    • struct、enum包含数据
    • impl块为之提供了方法
    • 便带有方法的struct、enum并没有被称为对象
  • 封装
    • 封装:调用对象外部的代码无法直接访问对象内部的实现细节,唯一可以与对象进行交互的方法就是通过它公开的api
    • Rust:pub关键字
  • 继承
    • 继承:使对象可以沿用另外一个对象的数据和行为,且无需重复定义相关代码
    • Rust: 没有继承
    • 使用继承的原因:
      • 代码复用
        • Rust: 默认trait方法来进行代码共享
      • 多态
        • Rust: 泛型和trait约束(限定参数化多态 bounded parametric)
    • 很多新语言都不使用继承作为内置的程序设计方案了

使用trait对象来存储不同类型的值

  • 有这样的一个需求
    • 创建一个GUI工具
      • 它会遍历某个元素的列表,依次调用元素的draw方法进行绘制
      • 例如:button、textfield等元素
    • 在面向对象语言里:
      • 定义一个component父类,里面定义了draw方法
      • 定义button、textfield等类,继承与component类
  • 为共有行为定义一个trait
    • rust 避免将struct或enum称为对象,因为它们与impl块是分开的
    • trait对象有些糇于其它语言中的对象:
      • 它们某种程度上组合了数据与行为
    • trait对象与传统对象不同的地方:
      • 无法为trait对象添加数据
    • trait对象被专门用于抽象某些共有行为,它没其它语言中的对象那么通用
    use gui::Draw;
    
      struct SelectBox {
          width: u32,
          height: u32,
          options: Vec<String>,
      }
    
      impl Draw for SelectBox {
          fn draw(&self) {
              // code to actually draw a select box
          }
      }
    
      use gui::{Button, Screen};
    
      fn main() {
          let screen = Screen {
              components: vec![
                  Box::new(SelectBox {
                      width: 75,
                      height: 10,
                      options: vec![
                          String::from("Yes"),
                          String::from("Maybe"),
                          String::from("No"),
                      ],
                  }),
                  Box::new(Button {
                      width: 50,
                      height: 10,
                      label: String::from("OK"),
                  }),
              ],
          };
    
          screen.run();
      }
    
    
  • trait对象执行的是动态派发
    • 将trait约束作用于泛型时,rust编译器会执行单态化:
      • 编译器会为我们用来替换泛型类型参数的每一个具体类型生成对应函数和方法的非泛型实现。
    • 通过单态化生成的代码会执行表态派发(static dispatch),在编译过程中确定调用的具体方法
    • 动态派发(dynamic dispatch)
      • 无法在编译过程中确定你调用的究竟是哪一种方法
      • 编译器会产生额外的代码以便在运行时找出希望调用的方法
    • 使用trait对象,会执行动态派发:
      • 产生运行时开销
      • 阻止编译器内联方法代码,使得部分优化操作无法进行
  • trait对象必须保证对象安全
    • 只能把满足对象安全(Object-safe)的trait转化为trait对象
    • rust采用一系列fqmj来判定某个对象是否安全
      • 方法的返回类型不是self
      • 方法中不包含任何泛型类型参数

实现面向对象的设计模式

  • 状态模式
    • 状态模式(state pattern)是一种面向对象设计模式:
      • 一个值拥有的内部状态由数个状态对象(state object)表达而成,而值的行为则随着内部状态的改变而改变
    • 使用状态模式意味着:
      • 业务需需求变化时,不需要修改持有状态的值的代码,或者使用这个值的代码
      • 只需要更新状态对象内部的代码,以便改变其规则。或者增加一些新的状态对象
pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    // --snip--
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        ""
    }

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }

    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<dyn State>;
    fn approve(self: Box<Self>) -> Box<dyn State>;
}

struct Draft {}

impl State for Draft {
    // --snip--
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

struct PendingReview {}

impl State for PendingReview {
    // --snip--
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        Box::new(Published {})
    }
}

struct Published {}

impl State for Published {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}