24.面向对象编程特性

目录

  • 一、面向对象语言特征
    • 1.1 对象包含数据和行为
    • 1.2 封装
    • 1.3 继承作为类型系统与代码共享
  • 二、使用trait对象存储不同类型的值
    • 2.1 定义共有行为的trait
    • 2.2 实现trait
    • 2.3 trait对象执行动态派发
    • 2.4 trait对象必须保证对象安全
  • 三、面向对象设计模式
  • 四、状态模式的权衡取舍
  • 五、将状态和行为编译为类型

一、面向对象语言特征

1.1 对象包含数据和行为

  • 面向对象的程序是由对象组成的;
  • 一个对象包含数据操作这些数据的过程。这些过程通常被称为方法或操作
  • Rust对象是面向对象的
    • 结构体和枚举包含数据;
    • impl块提供了方法;
    • 带有方法的struct,enum并没有被称为对象;

1.2 封装

  • 封装: 对象的实现细节不能被使用对象的代码获取,唯一与对象交互的方式是通过对象提供的公有API;
  • Rust里可以使用pub关键字来决定模块、类型、函数和方法是公有的(默认情况下一切都是私有的);
pub struct AveragedCollection {list: Vec<i32>,average: f64,
}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;}
}
  • AveragedCollection结构体是公有的,结构体内部的字段仍然是私有的;
  • 公有方法add、remove是修改AveragedCollection实例的唯一方式;
  • 已经封装好AveragedCollection的实现细节,可以轻松改变类似数据结构的内容;
  • 例如使用HashSet<i32>代替Vec<i32>成为list字段的类型;

1.3 继承作为类型系统与代码共享

  • Rust中无法定义一个结构体继承父结构体的成员和方法,因此没有继承的概念;
  • Rust可以使用默认trait方法实现来进行代码共享;
  • Rust可以使用泛型和trait约束实现多态;

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

  • 创建GUI工具:遍历某个元素的列表,依次调用元素的draw方法进行绘制;

2.1 定义共有行为的trait

  • Rust避免将struct和enum称为对象,因此它们与impl是分开的;
  • trait对象有些类似于其它语言中的对象,它们某种程度上组合了数据与行为;
  • 无法为trait对象添加数据;
  • trait对象被专门用于抽象某些共有行为,没有其它语言的对象那么通用;
pub trait Draw {fn draw(&self);
}pub struct Screen {pub components: Vec<Box<dyn Draw>>,
}impl Screen {pub fn run(&self) {for component in self.components.iter() {component.draw();}}
}
  • 定义了一个Draw train,内含方法draw
  • 定义结构体Screen,内有公共元素components,类型为Vector,里面放了Box<dyn Draw>,表示Box里的元素都实现了Draw的trait;
  • 可以用泛型表示;
pub struct Screen<T: Draw> {pub components: Vec<T>,
}
  • 泛型只能放入一种类型,但是Vec<Box<dyn Draw>>可以放所有实现了Draw的trait;

2.2 实现trait

  • 提供Button类型,实现Draw trait;
pub struct Button {pub width: u32,pub height: u32,pub label: String,
}impl Draw for Button {fn draw(&self) {// 实际绘制按钮的代码}
}
  • Button 上的 width、height 和 label 字段会和其他组件不同;
  • 上述代码都在lib.rs中,下面这个在main.rs
use testrust::Draw;
use testrust::{Screen, Button};struct SelectBox {width: u32,height: u32,options: Vec<String>,
}impl Draw for SelectBox {fn draw(&self) {// code to actually draw a select box}
}
  • 创建SelectBox结构体,并且也实现了Draw trait;
  • 在main函数中创建一个Screen实例;
  • 可以通过将SelectBoxButton放入Box<T>转变为trait对象来增加组件;
  • 接着调用Screen的 run 方法,它会调用每个组件的 draw 方法;
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();
}
  • 实现了draw的结构体都可以添加到Vector中;
  • 最后调用run进行绘制;

2.3 trait对象执行动态派发

  • 对泛型使用trait bound时编译器所进行单态化处理
    • 编译器为每一个被泛型类型参数代替的具体类型生成了非泛型的函数和方法实现。
    • 单态化所产生的代码进行静态分发(static dispatch),静态分发发生于编译器在编译时就知晓调用了什么方法;
    • 动态分发(dynamic dispatch) 无法在编译过程中确定调用的方法,编译器会生成在运行时确定调用了什么方法的代码;
  • trait对象执行动态派发
    • 会产生运行时开销;
    • 阻止编译器内联方法代码,使得部分优化操作无法进行;

2.4 trait对象必须保证对象安全

  • 只有对象安全(object safe) 的 trait 才可以组成 trait 对象;
  • trait中所有的方法有如下属性时,可以被认为是对象安全的:
    • 返回值类型不为Self;
    • 方法没有任何泛型类型参数;
  • 标准库中的Clone trait就不是对象安全的;
pub trait Clone {fn clone(&self) -> Self;
}

三、面向对象设计模式

  • 状态模式(state pattern) 是一个面向对象设计模式;
    • 一个值有某些内部状态,体现为一系列的状态对象,同时值的行为随着其内部状态而改变。
  • 使用状态模式时
    • 当程序的业务需求改变时,无需修改保存状态值的代码或使用值的代码;
    • 只需更新某个状态对象内部的代码,即可改变其规则,也可以增加更多的状态对象;
  • 一个增量式的发布博文的工作流:
    1. 博文从空白的草案开始;
    2. 一旦草案完成,请求审核博文;
    3. 一旦博文过审,它将被发表;
    4. 只有被发表的博文的内容会被打印;

主逻辑

只有main函数位于main.rs中,其它皆位于lib.rs

use blog::Post;fn main() {let mut post = Post::new();post.add_text("I ate a salad for lunch today");assert_eq!("", post.content());post.request_review();assert_eq!("", post.content());post.approve();assert_eq!("I ate a salad for lunch today", post.content());
}
  • 创建Post,然后使用add_text函数添加内容;
  • 接着使用request_review函数进行审批;
  • 最后使用approve函数进行审批;
  • 审批通过后可以使用content函数查看文件内容;

定义状态与添加/获取内容

  • 与crate交叉的唯一的类型是Post;
  • Post存放着博文当前所处的三种状态:草案(Draft)等待审核(PendingReview)发布(Published)
  • 定义Post结构体和new函数;
pub struct Post {state: Option<Box<dyn State>>,content: String,
}impl Post {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 {""}
}trait State{}struct Draft{}impl State for Draft {}
  • Post结构体里放实现了State的类型
  • 新建的博文状态(state)是个草案(Box::new(Draft {})),内容是空的(String::new()),博文的初始状态是私有的,因此外部无法改变其值;
  • State trait定义了所有不同状态的博文所共享的行为,同时三种状态都会实现State状态;
  • 通过Post的add_text方法修改博客内容;
  • 由于博客还是草案状态,因此返回的博客内容(content函数)应该是空的;

状态修改

  • 增加博文的审核功能;
  • 将其状态由Draft改为PendingReview
impl Post {pub fn request_review(&mut self) {if let Some(s) = self.state.take() {self.state = Some(s.request_review())}}
}trait State {fn request_review(self: Box<Self>) -> Box<dyn State>;
}struct Draft {}impl State for Draft {fn request_review(self: Box<Self>) -> Box<dyn State> {Box::new(PendingReview {})}
}struct PendingReview {}impl State for PendingReview {fn request_review(self: Box<Self>) -> Box<dyn State> {self}
}
  • Post中增加request_review方法,调用状态内部的同名方法,并返回一个新状态,目前草案状态调整为待审批状态、待审批状态则返回自身;
  • State trait中的request_review方法参数表示该方法只可在持有这个类型的Box上被调用,这个语法获取了Box<Self>的所有权使旧状态无效,以便Post的状态值可转移为一个新状态;
  • self.state.take()指将state字符中的some值取出使之成为 None,然后重新赋值;

增加审批方法

审批( request_review)方法会将state设置为发布状态;

impl Post {// --snip--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 approve(self: Box<Self>) -> Box<dyn State> {self}
}struct PendingReview {}impl State for PendingReview {// --snip--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}
}
  • 代码逻辑基本和前面的相同;
  • 不同的状态只需要维护自己的代码即可;

更新Post的content方法: 如果状态为 Published则返回博文 content 字段的值;否则返回空字符串切片;

impl Post {pub fn content(&self) -> &str {self.state.as_ref().unwrap().content(self)}
}
trait State {fn content<'a>(&self, post: &'a Post) -> &'a str {""}
}struct Published {}impl State for Published {fn content<'a>(&self, post: &'a Post) -> &'a str {&post.content}
}
  • Post的content中调用Option的as_ref方法是需要Option中值的引用而并非所有权;

四、状态模式的权衡取舍

  • 缺点在于某些状态之间是相互耦合的且需要重复实现一些逻辑代码

五、将状态和行为编译为类型

  • Rust类型检查系统会通过编译时错误来阻止用户使用无效状态;
pub struct Post {content: String,
}pub struct DraftPost {content: String,
}impl Post {pub fn new() -> DraftPost {DraftPost {content: String::new(),}}pub fn content(&self) -> &str {&self.content}
}impl DraftPost {pub fn add_text(&mut self, text: &str) {self.content.push_str(text);}pub fn request_review(self) -> PendingReviewPost {PendingReviewPost {content: self.content,}}
}pub struct PendingReviewPost {content: String,
}impl PendingReviewPost {pub fn approve(self) -> Post {Post {content: self.content,}}
}
  • Post指发布成功之后的,DraftPost就是草稿;
  • 当new一个Post时,返回的就是空草稿;
  • 草稿可以添加博文,可以请求审批,请求审批返回待审批的Post(PendingReviewPost );
  • 待审批的Post只有一个approve方法,因此它只需要审批,审批之后返回发布成功的Post;
  • 因此不同的Post只能做相应的事情,没有多余的方法;

main方法

use blog::Post;fn main() {let mut post = Post::new();post.add_text("I ate a salad for lunch today");let post = post.request_review();let post = post.approve();assert_eq!("I ate a salad for lunch today", post.content());
}
  • 从main函数看出来流程:创建草稿,添加内容,请求审核,获取审批通过的内容;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/34518.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python的 pyside2 安装

pip install pyside2 pip install pyqt5-tools pycharm 在pychar 的Main Menu--setings--tool--External-tools 点击 新增自定义工具 1&#xff09;自定义 QtDesigner 目的&#xff1a;用于生成.ui文件Name &#xff1a;QtDesigner Group &#xff1a;Qt Program &a…

交通 | 机器学习 + 大规模TSP/VRP求解

封面图来源&#xff1a;https://xkcd.com/399/ 推文作者&#xff1a;丁建辉&#xff0c;陈泰劼&#xff0c;张云天 本文针对旅行商问题&#xff08;Travelling salesman problem, TSP&#xff09;和车辆路径规划问题&#xff08;Vehicle routing problem, VRP&#xff09;这一类…

Python基础技能

目录 1. 掌握基础技能2. 变量与数据类型3. 条件语句4. 循环结构for循环while循环 5. 函数定义6. 列表与元组列表元组 1. 掌握基础技能 2. 变量与数据类型 在Python中&#xff0c;变量是用来存储数据的容器。我们可以给变量赋值&#xff0c;并使用这些值进行计算或操作。Pytho…

Excel 宏录制与VBA编程 —— 12、文本字符串类型相关(附示例)

字符串分割&#xff0c;文末示例&#xff08;文末代码3附有源码&#xff09; 代码1 - 基础字符串 代码2 - 字符串拆分 代码3 - 字符串分割 Option ExplicitSub WorkbooksClear()Dim DataRange As RangeSet DataRange Range("C2:E12")DataRange.Clear End SubSub Wo…

分布式系统_负载均衡

概述 大型网站都要面对庞大的用户量&#xff0c;高并发&#xff0c;海量数据等挑战。 为了提升系统整体的性能&#xff0c;可以采用垂直扩展和水平扩展两种方式。 垂直扩展&#xff1a;从单机角度扩展&#xff0c;增加单机硬件&#xff08;CPU、内存、磁盘&#xff09;处理能…

Vue进阶之Vue无代码可视化项目(五)

Vue无代码可视化项目 编排引擎smooth-dndLeftPanel.vueLayoutView.vuestores/debug.tsstores/editor.tsAppNavigator.vue添加-左侧栏添加到中间部分LayoutView.vuestore/editor.tsLeftPanel.vue移动-中间部分区域的位置更改新建文件夹utils、文件array.tsarray.tsLayoutView.vu…

基于rouyi框架的多租户改造

基于rouyi框架的多租户改造&#xff0c;重点是实现权限管理和数据隔离。权限管理相当于从原来的“顶级管理员admin-普通用户user”转变为“顶级管理员admin-租户管理员tanantAdmin-普通用户user”。数据隔离主要通过分库、分表、表内设置tenantId字段进行过滤三种方式。 本文主…

[word] word 如何在文档中进行分栏排版? #媒体#其他#媒体

word 如何在文档中进行分栏排版&#xff1f; 目标效果 将唐代诗人李白的组诗作品《清平调词》进行分栏排版&#xff0c;共分三栏&#xff0c;每一首诗作为一栏&#xff0c;参考效果如下图。

计算机图形学入门16:阴影映射

1.前言 前面几篇关于光栅化的文章中介绍了如何计算物体表面的光照&#xff0c;但是着色并不会进行阴影的计算&#xff0c;阴影需要单独进行处理&#xff0c;目前最常用的阴影计算技术之一就是Shadow Mapping技术&#xff0c;也就是俗称的阴影映射技术。 2.阴影映射 Shadow Map…

CSS中实现元素水平垂直居中的方式有哪些

在CSS中实现元素水平垂直居中的方法有很多&#xff0c;以下是一些常见的方法&#xff1a; 1. 使用Flexbox Flexbox是一个现代的布局模型&#xff0c;可以轻松实现元素的水平垂直居中。 .container {display: flex;justify-content: center; /* 水平居中 */align-items: cent…

C++在VS2022开发Windows窗口程序2:API式的Windows窗口程序设计模式

函数API式的Windows GUI程序设计模式是一种基于Windows API函数的方式来设计和开发Windows图形用户界面&#xff08;GUI&#xff09;应用程序的模式。在这种模式下&#xff0c;开发者通过调用Windows API函数来创建窗口、处理消息、绘制图形等&#xff0c;而不依赖于特定的GUI库…

mass storage:RAID Structure , Error Detection and Correction

RAID Structure RAID – redundant array of inexpensive disks multiple disk drives provides reliability via redundancyIncreases the mean time to failureMean time to repair – exposure time when another failure could cause data lossMean time to data loss bas…

【TB作品】stm32单片机,红外遥控器,温控风扇,模拟空调,PWM风扇

空调机 硬件&#xff1a;stm32、oled显示器、ds18b20温度传感器、风扇驱动和风扇、红外接收器、遥控器 软件功能&#xff1a; &#xff08;1&#xff09;显示室内温度 &#xff08;2&#xff09;显示当前模式&#xff1a;常态、除湿、通风 &#xff08;3&#xff09;显示当前风…

Ubuntu Apache2 搭建Gerrit 环境

一、前言 时隔多年&#xff0c;好久没有更新CSDN 博客了&#xff0c;主要原因有如下两点&#xff1a; 1、平时工作繁忙&#xff0c;无暇更新。 2、工作内容涉及信息安全&#xff0c;一些工作经验积累不便更新到互联网上。 最近一直在折腾搭建Gerrit 环境&#xff0c;最开始…

win11安装VMware虚拟机,启动系统后蓝屏,安装虚拟机卡在虚拟网卡界面的解决办法

机缘和遇到的问题 由于最近618换了台新笔记本电脑&#xff0c;然后系统自带的操作系统是windows 11 家庭版本&#xff0c;由于工作需要用到window10的环境&#xff0c;不得不安装一个虚拟机来解决问题&#xff0c;然后就把这次安装VMware虚拟机遇到的坑给大家分享一下&#xf…

研二自学嵌入式开发,就业导向,学习路线该如何规划?

研二才来问这个问题&#xff0c;有点晚&#xff0c;离你开始找工作还有大概8&#xff5e;9个月&#xff0c;你应该用应试思维来应对找工作这个事&#xff0c;尤其当前这个经济形势下。 刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入…

基于Openmv的追小球的云台

介绍 在这篇文章&#xff0c;我会先介绍需要用到且需要注意的函数&#xff0c;之后再给出整体代码 在追小球的云台中&#xff0c;比较重要的部分就是云台&#xff08;实质上就是舵机&#xff09;的控制以及对识别的色块位置进行处理得到相应信息后控制云台进行运动 1、舵机模…

asp.net core反向代理

新建项目 新建空白的asp.net core web项目 安装Yarp.ReverseProxy包版本为2.2.0-preview.1.24266.1 编写代码 namespace YarpStu01;public class Program {public static void Main(string[] args){var builder WebApplication.CreateBuilder(args);builder.Services.AddRev…

JavaWeb——MySQL:DQL

3. DQL:查询 查询是使用最多、最频繁的操作&#xff0c;因为前面的修改以及删除&#xff0c;一般会交给数据库专业的人员&#xff0c;对于非数据库专业人员来说&#xff0c;老板一般会放心的让你对数据库只进行查询操作&#xff1b; 3.2 条件查询&#xff08;where&#xff09…

VBA 实现EXCEl的sheet转换成PDF

首先&#xff0c;打开Excel&#xff0c;然后按下 Alt F11 打开VBA编辑器。在VBA编辑器中&#xff0c;点击 插入 -> 模块&#xff0c;创建一个新的模块。在新模块中&#xff0c;复制并粘贴以下VBA代码&#xff1a; Sub ExportEachSheetToPDF()Dim ws As WorksheetDim pdfFo…