外界有多纷扰,内心就有多丰饶
大家好,我是柒八九。
今天,我们继续Rust学习笔记的探索。我们来谈谈关于面向对象编程的相关知识点。
如果,想了解该系列的文章,可以参考我们已经发布的文章。如下是往期文章。
文章list
- Rust学习笔记之Rust环境配置和入门指南
- Rust学习笔记之基础概念
- Rust学习笔记之所有权
- Rust学习笔记之结构体
- Rust学习笔记之枚举和匹配模式
- Rust学习笔记之包、Crate和模块
- Rust学习笔记之集合
- Rust学习笔记之错误处理
- Rust学习笔记之泛型、trait 与生命周期
- Rust学习笔记之闭包和迭代器
- Rust学习笔记之智能指针
- Rust学习笔记之并发
你能所学到的知识点
- 编程范式 推荐阅读指数 ⭐️⭐️⭐️⭐️
- 面向对象编程 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
- 面向对象在Rust中的体现 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
好了,天不早了,干点正事哇。
编程范式
编程范式是一种编程的方法论或思维模式,它定义了一组规则、原则和约束,用于组织和结构化程序的设计和实现。
不同的编程范式提供了不同的思考方式和工作方式,以解决特定类型的问题和应对不同的编程场景。每种编程范式都基于一套独特的概念和原则,它们描述了程序员如何构建、组织和操作代码。编程范式指导程序员思考问题的方式,并提供了特定的语法和语义规则来实现代码。通过遵循特定的编程范式,程序员可以更好地组织代码、提高代码的可读性、可维护性和重用性。
常见的编程范式
编程范式 | 适用场景 | 主要编程语言 |
面向过程编程(Procedural Programming) | 基于过程和函数的编程范式,通过定义一系列的过程和函数来实现程序的逻辑 | C、Fortran、Pascal |
面向对象编程(Object-Oriented Programming,OOP) | 将现实世界的事物抽象成对象,通过对象之间的交互和消息传递来实现程序的设计和开发。 | Java、C++、Python、Ruby |
函数式编程(Functional Programming) | 将计算视为函数求值的过程,强调无状态和不可变数据的使用,避免副作用,注重函数的组合和高阶函数的使用 | Haskell、Lisp、Clojure、Scala |
声明式编程(Declarative Programming) | 描述问题的逻辑和规则,而不是明确指定解决问题的步骤,更关注"是什么"而非"如何" | SQL、Prolog、XSLT、Haskell |
响应式编程(Reactive Programming) | 基于数据流和事件的编程范式,通过触发和响应事件来处理数据流和状态变化。 前端开发、事件驱动、用户界面交互、异步数据流处理 |
JavaScript、RxJava、React |
过程式编程(Procedural Programming) | 按照顺序执行指令的编程方式,强调控制流和算法的设计 | Bash、Perl、Lua、Batch |
指令式编程(Imperative Programming) | 通过一系列的指令和语句来描述计算机的操作,强调修改和控制计算机状态。 | Assembly、C、C++ |
需要注意的是,编程范式并不是互斥的,而是可以相互融合和组合使用的。
例如,可以在面向对象编程中结合函数式编程的思想,或者在声明式编程中嵌入面向对象的概念。这种灵活的组合可以根据问题的需求和开发团队的偏好来决定。
面向对象编程
{面向对象编程|Object-Oriented Programming}(OOP
)是一种流行的编程范式,它以对象为核心,将数据和操作数据的方法封装在一起。面向对象编程强调对象之间的交互和消息传递,通过定义类、创建对象、继承和多态等概念来实现程序的设计和开发。
以下是面向对象编程的核心概念:
概念 | 描述 |
对象 (Object) | 面向对象编程的基本单元,是类的实例,具有自己的状态和行为。 |
类 (Class) | 定义对象的模板或蓝图,描述了对象的属性和行为。 |
封装 (Encapsulation) | 将数据和操作数据的方法封装在类中,隐藏内部实现细节,提供公共接口。 |
继承 (Inheritance) | 允许一个类继承另一个类的属性和方法,实现代码的重用和扩展。 |
多态 (Polymorphism) | 同一个方法可以根据对象的不同表现出不同的行为,提高代码的灵活性。 |
抽象 (Abstraction) | 将问题简化为类、对象和方法的模型,提取关键特征和行为形成可重用模型。 |
信息隐藏 (Information Hiding) | 将类的内部实现细节对外部隐藏,保护对象的状态,通过公共接口进行操作。 |
消息传递 (Message Passing) | 通过对象之间的消息传递实现交互,对象可以发送消息请求执行方法。 |
对象是类的实例,类定义了对象的属性和行为。封装将数据和方法封装在类中,继承允许类继承另一个类的属性和方法。多态使得同一个方法可以根据对象的不同表现出不同行为。抽象将问题简化为模型,信息隐藏保护对象的状态。消息传递实现对象之间的交互和协作。
面向对象编程具有以下优点:
- 可重用性:通过封装、继承和多态,可以实现代码的重用,减少开发工作量。
- 可维护性:面向对象编程具有模块化和组件化的特性,使代码更易于理解、修改和扩展。
- 灵活性:多态和抽象概念
面向对象在Rust中的体现
面向对象编程语言所共享的一些特性往往是{对象|Object}、{封装|Encapsulation}和{继承|Inheritance}。
对象包含数据和行为
Design Patterns
是面向对象编程模式的目录。它这样定义面向对象编程:
面向对象的程序是由对象组成的。一个对象包含数据和操作这些数据的过程。这些过程通常被称为方法或操作。
在这个定义下,Rust
是面向对象的:
结构体和枚举包含数据而
impl
块提供了在结构体和枚举之上的方法。虽然带有方法的结构体和枚举并不被称为对象,但是他们提供了与对象相同的功能。
封装隐藏了实现细节
另一个通常与面向对象编程相关的方面是 {封装|Encapsulation}的思想:对象的实现细节不能被使用对象的代码获取到。所以唯一与对象交互的方式是通过对象提供的公有 API;使用对象的代码无法深入到对象内部并直接改变数据或者行为。封装使得改变和重构对象的内部时无需改变使用对象的代码。
在
Rust
中,可以使用pub
关键字来决定模块、类型、函数和方法是公有的,而默认情况下其他一切都是私有的。
可以定义一个包含一个 i32
类型 vector
的结构体 AveragedCollection
。结构体也可以有一个字段,该字段保存了 vector
中所有值的平均值。
pub struct AveragedCollection { list: Vec<i32>, average: f64, }
结构体自身被标记为 pub
,这样其他代码就可以使用这个结构体,但是在结构体内部的字段仍然是私有的。
还可以在结构体上实现 add
、remove
和 average
方法。保证变量被增加到列表或者被从列表删除时,也会同时更新平均值。
impl AveragedCollection { pub fn add(&mut self, value: i32) { self.list.push(value); self.update_average(); } pub fn remove(&mut self) -> Option<i32> { let result = self.list.pop(); match result { Some(value) => { self.update_average(); Some(value) }, None => None, } } pub fn average(&self) -> f64 { self.average } fn update_average(&mut self) { let total: i32 = self.list.iter().sum(); self.average = total as f64 / self.list.len() as f64; } }
公有方法 add
、remove
和 average
是修改 AveragedCollection
实例的唯一方式。当使用 add
方法把一个元素加入到 list
或者使用 remove
方法来删除时,这些方法的实现同时会调用私有的 update_average
方法来更新 average
字段。
继承,作为类型系统与代码共享
{继承|Inheritance}是一个很多编程语言都提供的机制,一个对象可以定义为继承另一个对象的定义,这使其可以获得父对象的数据和行为,而无需重新定义。
如果一个语言必须有继承才能被称为面向对象语言的话,那么 Rust
就不是面向对象的。无法定义一个结构体继承父结构体的成员和方法。
选择继承有两个主要的原因。
- 第一个是为了重用代码:一旦为一个类型实现了特定行为,继承可以对一个不同的类型重用这个实现。相反
Rust
代码可以使用默认trait
方法实现来进行共享。- 第二个使用继承的原因与类型系统有关:表现为子类型可以用于父类型被使用的地方。这也被称为 {多态|polymorphism},这意味着如果多种对象共享特定的属性,则可以相互替代使用。
近来继承作为一种语言设计的解决方案在很多语言中失宠了,因为其时常带有共享多于所需的代码的风险。子类不应总是共享其父类的所有特征,但是继承却始终如此。如此会使程序设计更为不灵活,并引入无意义的子类方法调用,或由于方法实际并不适用于子类而造成错误的可能性。某些语言还只允许子类继承一个父类,进一步限制了程序设计的灵活性。
因为这些原因,Rust
选择了一个不同的途径,使用 trait
对象而不是继承。
为使用不同类型的值而设计的 trait 对象
Rust
刻意不将结构体与枚举称为 “对象”,以便与其他语言中的对象相区别。在结构体或枚举中,结构体字段中的数据和 impl
块中的行为是分开的,不同于其他语言中将数据和行为组合进一个称为对象的概念中。trait
对象将数据和行为两者相结合,从这种意义上说 则 其更类似其他语言中的对象。不过 trait
对象不同于传统的对象,因为不能向 trait
对象增加数据。trait
对象并不像其他语言中的对象那么通用:其(trait
对象)具体的作用是允许对通用行为进行抽象。
后记
分享是一种态度。
参考资料:《Rust权威指南》
全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。