第16章 面向对象编程特性
Rust面向对象编程特性摘要(146字): Rust通过模块系统、结构体和trait提供面向对象编程支持。封装特性体现在BankAccount示例中,私有字段通过公有方法访问。组合优于继承原则显著,如ContactInfo包含Address而非继承。Rust使用trait而非类继承实现多态,结构体方法提供行为封装。示例展示了存款/取款操作的安全封装模式,以及交易历史的受限访问。虽然不支持传统继承,

文章目录
第16章 面向对象编程特性
尽管Rust不是一门纯粹的面向对象语言,但它提供了强大的工具和模式来支持面向对象编程范式。Rust通过结构体、枚举、trait和模式匹配等特性,以独特的方式实现了封装、继承和多态等面向对象概念。本章将深入探讨Rust中的面向对象编程特性,包括如何使用trait对象实现多态、如何在Rust中实现常见的设计模式,以及如何在面向对象编程和Rust的固有风格之间找到平衡。
16.1 面向对象语言特性
封装与信息隐藏
封装是面向对象编程的基石,它允许隐藏对象的内部实现细节,只暴露必要的接口。Rust通过模块系统和可见性修饰符提供了强大的封装支持。
基本封装模式
pub struct BankAccount {
account_number: String,
balance: f64,
is_active: bool,
transaction_history: Vec<Transaction>,
}
impl BankAccount {
// 构造函数
pub fn new(account_number: String, initial_balance: f64) -> Self {
BankAccount {
account_number,
balance: initial_balance,
is_active: true,
transaction_history: Vec::new(),
}
}
// 公开的getter方法
pub fn account_number(&self) -> &str {
&self.account_number
}
pub fn balance(&self) -> f64 {
self.balance
}
pub fn is_active(&self) -> bool {
self.is_active
}
// 受控的修改方法
pub fn deposit(&mut self, amount: f64) -> Result<(), String> {
if !self.is_active {
return Err("账户已冻结".to_string());
}
if amount <= 0.0 {
return Err("存款金额必须为正数".to_string());
}
self.balance += amount;
self.transaction_history.push(Transaction::deposit(amount));
Ok(())
}
pub fn withdraw(&mut self, amount: f64) -> Result<f64, String> {
if !self.is_active {
return Err("账户已冻结".to_string());
}
if amount <= 0.0 {
return Err("取款金额必须为正数".to_string());
}
if amount > self.balance {
return Err("余额不足".to_string());
}
self.balance -= amount;
self.transaction_history.push(Transaction::withdrawal(amount));
Ok(amount)
}
// 内部实现细节
fn add_interest(&mut self, rate: f64) {
let interest = self.balance * rate;
self.balance += interest;
self.transaction_history.push(Transaction::interest(interest));
}
// 提供有限的交易历史访问
pub fn recent_transactions(&self, count: usize) -> Vec<&Transaction> {
self.transaction_history
.iter()
.rev()
.take(count)
.collect()
}
}
#[derive(Debug, Clone)]
struct Transaction {
kind: TransactionType,
amount: f64,
timestamp: std::time::SystemTime,
}
#[derive(Debug, Clone)]
enum TransactionType {
Deposit,
Withdrawal,
Interest,
Transfer,
}
impl Transaction {
fn deposit(amount: f64) -> Self {
Transaction {
kind: TransactionType::Deposit,
amount,
timestamp: std::time::SystemTime::now(),
}
}
fn withdrawal(amount: f64) -> Self {
Transaction {
kind: TransactionType::Withdrawal,
amount,
timestamp: std::time::SystemTime::now(),
}
}
fn interest(amount: f64) -> Self {
Transaction {
kind: TransactionType::Interest,
amount,
timestamp: std::time::SystemTime::now(),
}
}
}
fn encapsulation_demo() {
println!("=== 封装与信息隐藏演示 ===");
let mut account = BankAccount::new("123456789".to_string(), 1000.0);
println!("账户号码: {}", account.account_number());
println!("初始余额: {:.2}", account.balance());
println!("账户状态: {}", if account.is_active() { "活跃" } else { "冻结" });
// 存款操作
match account.deposit(500.0) {
Ok(()) => println!("存款成功"),
Err(e) => println!("存款失败: {}", e),
}
// 取款操作
match account.withdraw(200.0) {
Ok(amount) => println!("取款成功: {:.2}", amount),
Err(e) => println!("取款失败: {}", e),
}
// 尝试非法操作
match account.withdraw(-100.0) {
Ok(_) => println!("这不应该发生"),
Err(e) => println!("预期中的错误: {}", e),
}
println!("最终余额: {:.2}", account.balance());
// 查看最近交易
println!("最近交易:");
for tx in account.recent_transactions(3) {
println!(" {:?} - {:.2}", tx.kind, tx.amount);
}
// 无法直接访问私有字段
// println!("直接访问: {}", account.balance); // 编译错误
}
组合优于继承
Rust不支持传统的类继承,而是鼓励使用组合和trait来实现代码复用和多态。
组合模式示例
// 使用组合构建复杂对象
struct Address {
street: String,
city: String,
postal_code: String,
country: String,
}
impl Address {
fn new(street: &str, city: &str, postal_code: &str, country: &str) -> Self {
Address {
street: street.to_string(),
city: city.to_string(),
postal_code: postal_code.to_string(),
country: country.to_string(),
}
}
fn format(&self) -> String {
format!(
"{}, {}, {} {}",
self.street, self.city, self.postal_code, self.country
)
}
}
struct ContactInfo {
email: String,
phone: String,
address: Address, // 组合:ContactInfo包含Address
}
impl ContactInfo {
fn new(email: &str, phone: &str, address: Address) -> Self {
ContactInfo {
email: email.to_string(),
phone: phone.to_string(),
address,
}
}
fn display(&self) {
println!("邮箱: {}", self.email);
println!("电话: {}", self.phone);
println!("地址: {}", self.address.format());
}
}
struct Customer {
id: u32,
name: String,
contact: ContactInfo, // 组合:Customer包含ContactInfo
loyalty_points: u32,
}
impl Customer {
fn new(id: u32, name: &str, contact: ContactInfo) -> Self {
Customer {
id,
name: name.to_string(),
contact,
loyalty_points: 0,
}
}
fn add_loyalty_points(&mut self, points: u32) {
self.loyalty_points += points;
println!("{} 获得 {} 积分,总计: {}", self.name, points, self.loyalty_points);
}
fn display_profile(&self) {
println!("客户 ID: {}", self.id);
println!("姓名: {}", self.name);
println!("积分: {}", self.loyalty_points);
self.contact.display();
}
}
fn composition_demo() {
println!("=== 组合优于继承演示 ===");
let address = Address::new(
"123 Rust Street",
"Systems City",
"10001",
"Programland"
);
let contact = ContactInfo::new(
"alice@example.com",
"+1-555-0123",
address
);
let mut customer = Customer::new(1, "Alice", contact);
customer.display_profile();
println!();
customer.add_loyalty_points(100);
customer.add_loyalty_points(50);
println!("\n完整的客户信息:");
customer.display_profile();
}
方法语法与关联函数
Rust使用impl块为结构体和枚举定义方法,提供了面向对象风格的方法调用语法。
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// 关联函数(类似于静态方法)
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
fn square(size: f64) -> Self {
Rectangle {
width: size,
height: size,
}
}
// 实例方法
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
fn can_contain(&self, other: &Rectangle) -> bool {
self.width >= other.width && self.height >= other.height
}
// 可变方法
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
// 获取所有权的方法
fn combine(self, other: Rectangle) -> Rectangle {
Rectangle {
width: self.width + other.width,
height: self.height.max(other.height),
}
}
}
// 为同一类型实现多个impl块
impl Rectangle {
fn aspect_ratio(&self) -> f64 {
self.width / self.height
}
fn is_square(&self) -> bool {
(self.width - self.height).abs() < f64::EPSILON
}
}
fn method_syntax_demo() {
println!("=== 方法语法演示 ===");
let rect1 = Rectangle::new(10.0, 20.0);
let rect2 = Rectangle::square(15.0);
println!("矩形1 - 面积: {:.2}, 周长: {:.2}", rect1.area(), rect1.perimeter());
println!("矩形2 - 面积: {:.2}, 周长: {:.2}", rect2.area(), rect2.perimeter());
println!("矩形1能包含矩形2: {}", rect1.can_contain(&rect2));
println!("矩形2是正方形: {}", rect2.is_square());
println!("矩形1宽高比: {:.2}", rect1.aspect_ratio());
let mut scalable_rect = Rectangle::new(5.0, 10.0);
println!("缩放前: {}x{}", scalable_rect.width, scalable_rect.height);
scalable_rect.scale(2.0);
println!("缩放后: {}x{}", scalable_rect.width, scalable_rect.height);
let combined = rect1.combine(rect2);
println!("合并后的矩形: {}x{}", combined.width, combined.height);
}
16.2 使用trait对象存储不同类型值
Trait对象基础
Trait对象允许我们在运行时处理多种类型,是实现多态的重要手段。它们使用动态分发,在牺牲少量性能的同时提供了极大的灵活性。
基本Trait对象使用
trait Drawable {
fn draw(&self);
fn area(&self) -> f64;
fn description(&self) -> String {
"一个可绘制对象".to_string()
}
}
struct Circle {
radius: f64,
center: (f64, f64),
}
impl Drawable for Circle {
fn draw(&self) {
println!("绘制圆形: 半径={}, 中心点=({}, {})",
self.radius, self.center.0, self.center.1);
}
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn description(&self) -> String {
format!("圆形 (半径: {})", self.radius)
}
}
struct Rectangle {
width: f64,
height: f64,
position: (f64, f64),
}
impl Drawable for Rectangle {
fn draw(&self) {
println!("绘制矩形: {}x{}, 位置=({}, {})",
self.width, self.height, self.position.0, self.position.1);
}
fn area(&self) -> f64 {
self.width * self.height
}
fn description(&self) -> String {
format!("矩形 ({}x{})", self.width, self.height)
}
}
struct Triangle {
base: f64,
height: f64,
vertices: [(f64, f64); 3],
}
impl Drawable for Triangle {
fn draw(&self) {
println!("绘制三角形: 底边={}, 高={}", self.base, self.height);
}
fn area(&self) -> f64 {
0.5 * self.base * self.height
}
fn description(&self) -> String {
format!("三角形 (底: {}, 高: {})", self.base, self.height)
}
}
fn trait_objects_basics() {
println!("=== Trait对象基础 ===");
// 创建不同类型的对象
let circle = Circle { radius: 5.0, center: (10.0, 10.0) };
let rectangle = Rectangle { width: 8.0, height: 6.0, position: (20.0, 20.0) };
let triangle = Triangle { base: 10.0, height: 7.0, vertices: [(0.0, 0.0), (10.0, 0.0), (5.0, 7.0)] };
// 使用trait对象存储不同类型
let shapes: Vec<Box<dyn Drawable>> = vec![
Box::new(circle),
Box::new(rectangle),
Box::new(triangle),
];
// 统一处理不同类型的对象
for shape in shapes {
println!("{}", shape.description());
shape.draw();
println!("面积: {:.2}", shape.area());
println!("---");
}
}
高级Trait对象模式
Trait对象可以用于构建复杂的多态系统,如插件架构、GUI框架等。
// 更复杂的trait对象示例:支付处理系统
trait PaymentProcessor {
fn process_payment(&self, amount: f64) -> Result<String, String>;
fn refund(&self, transaction_id: &str) -> Result<String, String>;
fn get_processor_name(&self) -> String;
fn supports_currency(&self, currency: &str) -> bool;
}
struct CreditCardProcessor {
api_key: String,
supported_currencies: Vec<String>,
}
impl CreditCardProcessor {
fn new(api_key: &str) -> Self {
CreditCardProcessor {
api_key: api_key.to_string(),
supported_currencies: vec!["USD".to_string(), "EUR".to_string(), "GBP".to_string()],
}
}
}
impl PaymentProcessor for CreditCardProcessor {
fn process_payment(&self, amount: f64) -> Result<String, String> {
if amount <= 0.0 {
return Err("金额必须大于0".to_string());
}
// 模拟API调用
let transaction_id = format!("CC_{}_{}", self.api_key, rand::random::<u32>());
Ok(format!("信用卡支付成功,交易ID: {}", transaction_id))
}
fn refund(&self, transaction_id: &str) -> Result<String, String> {
if !transaction_id.starts_with("CC_") {
return Err("无效的信用卡交易ID".to_string());
}
Ok(format!("信用卡退款成功,交易ID: {}", transaction_id))
}
fn get_processor_name(&self) -> String {
"信用卡支付处理器".to_string()
}
fn supports_currency(&self, currency: &str) -> bool {
self.supported_currencies.contains(¤cy.to_string())
}
}
struct PayPalProcessor {
email: String,
sandbox: bool,
}
impl PayPalProcessor {
fn new(email: &str, sandbox: bool) -> Self {
PayPalProcessor {
email: email.to_string(),
sandbox,
}
}
}
impl PaymentProcessor for PayPalProcessor {
fn process_payment(&self, amount: f64) -> Result<String, String> {
if amount < 1.0 {
return Err("PayPal支付最低金额为1.0".to_string());
}
let environment = if self.sandbox { "沙盒" } else { "生产" };
let transaction_id = format!("PP_{}_{}", self.email, rand::random::<u32>());
Ok(format!("PayPal支付成功 ({}), 交易ID: {}", environment, transaction_id))
}
fn refund(&self, transaction_id: &str) -> Result<String, String> {
if !transaction_id.starts_with("PP_") {
return Err("无效的PayPal交易ID".to_string());
}
Ok(format!("PayPal退款成功,交易ID: {}", transaction_id))
}
fn get_processor_name(&self) -> String {
"PayPal支付处理器".to_string()
}
fn supports_currency(&self, currency: &str) -> bool {
// PayPal支持更多货币
matches!(currency, "USD" | "EUR" | "GBP" | "CAD" | "AUD" | "JPY")
}
}
struct CryptoProcessor {
wallet_address: String,
network: String,
}
impl CryptoProcessor {
fn new(wallet_address: &str, network: &str) -> Self {
CryptoProcessor {
wallet_address: wallet_address.to_string(),
network: network.to_string(),
}
}
}
impl PaymentProcessor for CryptoProcessor {
fn process_payment(&self, amount: f64) -> Result<String, String> {
if amount < 0.001 {
return Err("加密货币支付最低金额为0.001".to_string());
}
let transaction_id = format!("CRYPTO_{}_{}", self.wallet_address, rand::random::<u32>());
Ok(format!("加密货币支付成功 ({}), 交易ID: {}", self.network, transaction_id))
}
fn refund(&self, _transaction_id: &str) -> Result<String, String> {
Err("加密货币支付不支持退款".to_string())
}
fn get_processor_name(&self) -> String {
format!("加密货币处理器 ({})", self.network)
}
fn supports_currency(&self, currency: &str) -> bool {
matches!(currency, "BTC" | "ETH" | "USDT")
}
}
// 支付处理管理器
struct PaymentManager {
processors: Vec<Box<dyn PaymentProcessor>>,
}
impl PaymentManager {
fn new() -> Self {
PaymentManager {
processors: Vec::new(),
}
}
fn add_processor(&mut self, processor: Box<dyn PaymentProcessor>) {
self.processors.push(processor);
}
fn process_with_currency(&self, amount: f64, currency: &str) -> Result<String, String> {
for processor in &self.processors {
if processor.supports_currency(currency) {
println!("使用: {}", processor.get_processor_name());
return processor.process_payment(amount);
}
}
Err(format!("没有找到支持货币 {} 的支付处理器", currency))
}
fn list_processors(&self) {
println!("可用的支付处理器:");
for (i, processor) in self.processors.iter().enumerate() {
println!(" {}. {}", i + 1, processor.get_processor_name());
}
}
}
fn advanced_trait_objects_demo() {
println!("=== 高级Trait对象演示 ===");
let mut payment_manager = PaymentManager::new();
// 注册不同的支付处理器
payment_manager.add_processor(Box::new(CreditCardProcessor::new("sk_test_123")));
payment_manager.add_processor(Box::new(PayPalProcessor::new("merchant@example.com", true)));
payment_manager.add_processor(Box::new(CryptoProcessor::new("0x742d35Cc...", "Ethereum")));
payment_manager.list_processors();
println!();
// 测试不同货币的支付
let test_cases = vec![
(100.0, "USD"),
(50.0, "EUR"),
(0.1, "BTC"),
(25.0, "CAD"),
(0.0001, "ETH"), // 这个会失败,金额太小
];
for (amount, currency) in test_cases {
println!("尝试支付: {} {}", amount, currency);
match payment_manager.process_with_currency(amount, currency) {
Ok(result) => println!("成功: {}", result),
Err(error) => println!("失败: {}", error),
}
println!("---");
}
}
Trait对象的限制与解决方案
Trait对象有一些限制,了解这些限制及其解决方案对于有效使用它们至关重要。
fn trait_object_limitations() {
println!("=== Trait对象的限制与解决方案 ===");
// 限制1: Trait对象不能返回Self
trait Cloneable: Clone {
fn clone_me(&self) -> Self;
}
// 下面的代码无法编译,因为trait对象不能使用Self
// let objects: Vec<Box<dyn Cloneable>> = vec![];
// 解决方案: 使用Box<dyn Trait>
trait CloneableTrait {
fn clone_box(&self) -> Box<dyn CloneableTrait>;
}
impl<T> CloneableTrait for T
where
T: Clone + 'static,
{
fn clone_box(&self) -> Box<dyn CloneableTrait> {
Box::new(self.clone())
}
}
// 限制2: 只能使用对象安全的trait
// 对象安全的trait不能有泛型方法或返回Self的方法
// 解决方案: 将泛型方法拆分为不同的trait
trait GenericProcessor {
fn process<T: serde::Serialize>(&self, data: T) -> Result<String, String>;
}
// 这个trait不是对象安全的,因为有泛型方法
// let processors: Vec<Box<dyn GenericProcessor>> = vec![]; // 编译错误
trait ObjectSafeProcessor {
fn process_json(&self, json_data: &str) -> Result<String, String>;
}
// 这个trait是对象安全的
let _processors: Vec<Box<dyn ObjectSafeProcessor>> = vec![];
println!("了解trait对象的限制很重要!");
}
16.3 面向对象设计模式实现
工厂模式
工厂模式提供了一种创建对象的方式,而无需向客户端暴露实例化逻辑。
// 工厂模式示例:文档处理系统
trait Document {
fn open(&self);
fn save(&self);
fn close(&self);
fn get_type(&self) -> String;
}
struct TextDocument {
content: String,
file_path: String,
}
impl TextDocument {
fn new(content: &str, file_path: &str) -> Self {
TextDocument {
content: content.to_string(),
file_path: file_path.to_string(),
}
}
}
impl Document for TextDocument {
fn open(&self) {
println!("打开文本文档: {}", self.file_path);
println!("内容预览: {}...", &self.content[..self.content.len().min(50)]);
}
fn save(&self) {
println!("保存文本文档到: {}", self.file_path);
}
fn close(&self) {
println!("关闭文本文档: {}", self.file_path);
}
fn get_type(&self) -> String {
"text/plain".to_string()
}
}
struct PdfDocument {
file_path: String,
page_count: u32,
}
impl PdfDocument {
fn new(file_path: &str, page_count: u32) -> Self {
PdfDocument {
file_path: file_path.to_string(),
page_count,
}
}
}
impl Document for PdfDocument {
fn open(&self) {
println!("打开PDF文档: {}", self.file_path);
println!("页数: {}", self.page_count);
}
fn save(&self) {
println!("保存PDF文档到: {}", self.file_path);
}
fn close(&self) {
println!("关闭PDF文档: {}", self.file_path);
}
fn get_type(&self) -> String {
"application/pdf".to_string()
}
}
struct SpreadsheetDocument {
file_path: String,
sheet_count: u32,
}
impl SpreadsheetDocument {
fn new(file_path: &str, sheet_count: u32) -> Self {
SpreadsheetDocument {
file_path: file_path.to_string(),
sheet_count,
}
}
}
impl Document for SpreadsheetDocument {
fn open(&self) {
println!("打开电子表格: {}", self.file_path);
println!("工作表数量: {}", self.sheet_count);
}
fn save(&self) {
println!("保存电子表格到: {}", self.file_path);
}
fn close(&self) {
println!("关闭电子表格: {}", self.file_path);
}
fn get_type(&self) -> String {
"application/vnd.ms-excel".to_string()
}
}
// 文档工厂
enum DocumentType {
Text,
Pdf,
Spreadsheet,
}
struct DocumentFactory;
impl DocumentFactory {
fn create_document(doc_type: DocumentType, file_path: &str) -> Box<dyn Document> {
match doc_type {
DocumentType::Text => Box::new(TextDocument::new("默认内容", file_path)),
DocumentType::Pdf => Box::new(PdfDocument::new(file_path, 1)),
DocumentType::Spreadsheet => Box::new(SpreadsheetDocument::new(file_path, 1)),
}
}
fn create_document_from_extension(file_path: &str) -> Result<Box<dyn Document>, String> {
let extension = file_path
.rsplit('.')
.next()
.ok_or("无法确定文件扩展名")?;
match extension.to_lowercase().as_str() {
"txt" => Ok(Self::create_document(DocumentType::Text, file_path)),
"pdf" => Ok(Self::create_document(DocumentType::Pdf, file_path)),
"xlsx" | "xls" => Ok(Self::create_document(DocumentType::Spreadsheet, file_path)),
_ => Err(format!("不支持的文件类型: .{}", extension)),
}
}
}
fn factory_pattern_demo() {
println!("=== 工厂模式演示 ===");
// 使用工厂创建文档
let documents = vec![
DocumentFactory::create_document(DocumentType::Text, "document.txt"),
DocumentFactory::create_document(DocumentType::Pdf, "presentation.pdf"),
DocumentFactory::create_document(DocumentType::Spreadsheet, "data.xlsx"),
];
for doc in documents {
println!("文档类型: {}", doc.get_type());
doc.open();
doc.save();
doc.close();
println!("---");
}
// 根据文件扩展名创建文档
let file_paths = vec!["readme.txt", "manual.pdf", "budget.xlsx", "image.png"];
for file_path in file_paths {
println!("处理文件: {}", file_path);
match DocumentFactory::create_document_from_extension(file_path) {
Ok(doc) => {
doc.open();
doc.close();
}
Err(e) => println!("错误: {}", e),
}
println!("---");
}
}
观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
// 观察者模式示例:股票市场监控系统
type ObserverId = u64;
trait Observer {
fn update(&self, symbol: &str, price: f64);
fn get_id(&self) -> ObserverId;
}
trait Subject {
fn register_observer(&mut self, observer: Arc<dyn Observer>);
fn remove_observer(&mut self, observer_id: ObserverId);
fn notify_observers(&self, symbol: &str, price: f64);
}
// 具体的主题:股票
struct Stock {
symbol: String,
price: f64,
observers: Arc<Mutex<HashMap<ObserverId, Arc<dyn Observer>>>>,
next_observer_id: ObserverId,
}
impl Stock {
fn new(symbol: &str, initial_price: f64) -> Self {
Stock {
symbol: symbol.to_string(),
price: initial_price,
observers: Arc::new(Mutex::new(HashMap::new())),
next_observer_id: 1,
}
}
fn set_price(&mut self, new_price: f64) {
let old_price = self.price;
self.price = new_price;
if (new_price - old_price).abs() > f64::EPSILON {
println!("{} 价格变化: {:.2} -> {:.2}", self.symbol, old_price, new_price);
self.notify_observers(&self.symbol, new_price);
}
}
fn get_price(&self) -> f64 {
self.price
}
}
impl Subject for Stock {
fn register_observer(&mut self, observer: Arc<dyn Observer>) {
let observer_id = observer.get_id();
self.observers.lock().unwrap().insert(observer_id, observer);
self.next_observer_id += 1;
}
fn remove_observer(&mut self, observer_id: ObserverId) {
self.observers.lock().unwrap().remove(&observer_id);
}
fn notify_observers(&self, symbol: &str, price: f64) {
let observers = self.observers.lock().unwrap();
for observer in observers.values() {
observer.update(symbol, price);
}
}
}
// 具体的观察者:价格显示器
struct PriceDisplay {
id: ObserverId,
name: String,
}
impl PriceDisplay {
fn new(name: &str) -> Self {
static mut NEXT_ID: ObserverId = 1;
let id = unsafe {
let current = NEXT_ID;
NEXT_ID += 1;
current
};
PriceDisplay {
id,
name: name.to_string(),
}
}
}
impl Observer for PriceDisplay {
fn update(&self, symbol: &str, price: f64) {
println!("[{}] {}: {:.2}", self.name, symbol, price);
}
fn get_id(&self) -> ObserverId {
self.id
}
}
// 具体的观察者:价格警报器
struct PriceAlert {
id: ObserverId,
symbol: String,
threshold: f64,
alert_type: AlertType,
}
#[derive(PartialEq)]
enum AlertType {
Above,
Below,
}
impl PriceAlert {
fn new(symbol: &str, threshold: f64, alert_type: AlertType) -> Self {
static mut NEXT_ID: ObserverId = 1;
let id = unsafe {
let current = NEXT_ID;
NEXT_ID += 1;
current
};
PriceAlert {
id,
symbol: symbol.to_string(),
threshold,
alert_type,
}
}
}
impl Observer for PriceAlert {
fn update(&self, symbol: &str, price: f64) {
if symbol == self.symbol {
let should_alert = match self.alert_type {
AlertType::Above => price > self.threshold,
AlertType::Below => price < self.threshold,
};
if should_alert {
let direction = match self.alert_type {
AlertType::Above => "高于",
AlertType::Below => "低于",
};
println!("🚨 警报! {} {:.2} {} 阈值 {:.2}", symbol, price, direction, self.threshold);
}
}
}
fn get_id(&self) -> ObserverId {
self.id
}
}
fn observer_pattern_demo() {
println!("=== 观察者模式演示 ===");
let mut apple_stock = Stock::new("AAPL", 150.0);
let mut google_stock = Stock::new("GOOGL", 2800.0);
// 创建观察者
let display1 = Arc::new(PriceDisplay::new("主显示器"));
let display2 = Arc::new(PriceDisplay::new("移动端"));
let apple_high_alert = Arc::new(PriceAlert::new("AAPL", 160.0, AlertType::Above));
let apple_low_alert = Arc::new(PriceAlert::new("AAPL", 140.0, AlertType::Below));
let google_alert = Arc::new(PriceAlert::new("GOOGL", 2900.0, AlertType::Above));
// 注册观察者
apple_stock.register_observer(display1.clone());
apple_stock.register_observer(apple_high_alert.clone());
apple_stock.register_observer(apple_low_alert.clone());
google_stock.register_observer(display1.clone());
google_stock.register_observer(display2.clone());
google_stock.register_observer(google_alert.clone());
// 模拟价格变化
println!("模拟股票价格变化:");
apple_stock.set_price(155.0);
apple_stock.set_price(145.0); // 触发低价警报
apple_stock.set_price(165.0); // 触发高价警报
google_stock.set_price(2850.0);
google_stock.set_price(2950.0); // 触发Google警报
// 移除一个观察者
println!("\n移除移动端显示器后的变化:");
google_stock.remove_observer(display2.get_id());
google_stock.set_price(3000.0);
}
策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。
// 策略模式示例:支付处理系统
trait PaymentStrategy {
fn pay(&self, amount: f64) -> Result<String, String>;
fn get_name(&self) -> String;
}
struct CreditCardStrategy {
card_number: String,
expiry_date: String,
cvv: String,
}
impl CreditCardStrategy {
fn new(card_number: &str, expiry_date: &str, cvv: &str) -> Self {
CreditCardStrategy {
card_number: card_number.to_string(),
expiry_date: expiry_date.to_string(),
cvv: cvv.to_string(),
}
}
}
impl PaymentStrategy for CreditCardStrategy {
fn pay(&self, amount: f64) -> Result<String, String> {
if amount <= 0.0 {
return Err("金额必须大于0".to_string());
}
// 模拟信用卡处理
let masked_number = &self.card_number[self.card_number.len().saturating_sub(4)..];
Ok(format!("信用卡支付成功! 卡号: ****{} 金额: {:.2}", masked_number, amount))
}
fn get_name(&self) -> String {
"信用卡".to_string()
}
}
struct PayPalStrategy {
email: String,
}
impl PayPalStrategy {
fn new(email: &str) -> Self {
PayPalStrategy {
email: email.to_string(),
}
}
}
impl PaymentStrategy for PayPalStrategy {
fn pay(&self, amount: f64) -> Result<String, String> {
if amount < 1.0 {
return Err("PayPal支付最低金额为1.0".to_string());
}
Ok(format!("PayPal支付成功! 邮箱: {} 金额: {:.2}", self.email, amount))
}
fn get_name(&self) -> String {
"PayPal".to_string()
}
}
struct CryptoStrategy {
wallet_address: String,
}
impl CryptoStrategy {
fn new(wallet_address: &str) -> Self {
CryptoStrategy {
wallet_address: wallet_address.to_string(),
}
}
}
impl PaymentStrategy for CryptoStrategy {
fn pay(&self, amount: f64) -> Result<String, String> {
if amount < 0.001 {
return Err("加密货币支付最低金额为0.001".to_string());
}
let masked_address = if self.wallet_address.len() > 8 {
format!("{}...{}",
&self.wallet_address[..4],
&self.wallet_address[self.wallet_address.len()-4..])
} else {
self.wallet_address.clone()
};
Ok(format!("加密货币支付成功! 钱包: {} 金额: {:.4}", masked_address, amount))
}
fn get_name(&self) -> String {
"加密货币".to_string()
}
}
// 支付上下文
struct PaymentContext {
strategy: Box<dyn PaymentStrategy>,
}
impl PaymentContext {
fn new(strategy: Box<dyn PaymentStrategy>) -> Self {
PaymentContext { strategy }
}
fn set_strategy(&mut self, strategy: Box<dyn PaymentStrategy>) {
self.strategy = strategy;
}
fn execute_payment(&self, amount: f64) -> Result<String, String> {
println!("使用 {} 进行支付...", self.strategy.get_name());
self.strategy.pay(amount)
}
}
fn strategy_pattern_demo() {
println!("=== 策略模式演示 ===");
// 创建不同的支付策略
let credit_card = CreditCardStrategy::new("4111111111111111", "12/25", "123");
let paypal = PayPalStrategy::new("user@example.com");
let crypto = CryptoStrategy::new("0x1a2b3c4d5e6f7g8h9i0j");
let mut payment_context = PaymentContext::new(Box::new(credit_card));
// 使用信用卡支付
match payment_context.execute_payment(100.0) {
Ok(result) => println!("结果: {}", result),
Err(e) => println!("错误: {}", e),
}
// 切换到PayPal
payment_context.set_strategy(Box::new(paypal));
match payment_context.execute_payment(50.0) {
Ok(result) => println!("结果: {}", result),
Err(e) => println!("错误: {}", e),
}
// 切换到加密货币
payment_context.set_strategy(Box::new(crypto));
match payment_context.execute_payment(0.1) {
Ok(result) => println!("结果: {}", result),
Err(e) => println!("错误: {}", e),
}
// 测试边界情况
println!("\n测试边界情况:");
match payment_context.execute_payment(0.0001) {
Ok(result) => println!("结果: {}", result),
Err(e) => println!("错误: {}", e),
}
}
16.4 面向对象与Rust风格的权衡
Rust的哲学与面向对象编程
Rust的设计哲学与传统的面向对象语言有所不同,理解这些差异对于编写地道的Rust代码至关重要。
组合与继承的权衡
fn composition_vs_inheritance() {
println!("=== 组合与继承的权衡 ===");
// 传统OOP中的继承在Rust中通常用组合和trait实现
// 示例:图形编辑器中的形状层次结构
// 使用trait定义公共接口
trait Shape {
fn area(&self) -> f64;
fn perimeter(&self) -> f64;
fn draw(&self);
fn description(&self) -> String {
"一个形状".to_string()
}
}
// 使用组合构建复杂形状
struct Point {
x: f64,
y: f64,
}
impl Point {
fn new(x: f64, y: f64) -> Self {
Point { x, y }
}
fn distance_to(&self, other: &Point) -> f64 {
((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
}
}
// 简单的形状
struct Circle {
center: Point, // 组合:Circle包含Point
radius: f64,
}
impl Circle {
fn new(center: Point, radius: f64) -> Self {
Circle { center, radius }
}
}
impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn perimeter(&self) -> f64 {
2.0 * std::f64::consts::PI * self.radius
}
fn draw(&self) {
println!("绘制圆形: 中心({}, {}), 半径{}",
self.center.x, self.center.y, self.radius);
}
fn description(&self) -> String {
format!("圆形 (半径: {})", self.radius)
}
}
struct Rectangle {
top_left: Point, // 组合
bottom_right: Point, // 组合
}
impl Rectangle {
fn new(top_left: Point, bottom_right: Point) -> Self {
Rectangle { top_left, bottom_right }
}
fn width(&self) -> f64 {
(self.bottom_right.x - self.top_left.x).abs()
}
fn height(&self) -> f64 {
(self.bottom_right.y - self.top_left.y).abs()
}
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width() * self.height()
}
fn perimeter(&self) -> f64 {
2.0 * (self.width() + self.height())
}
fn draw(&self) {
println!("绘制矩形: 从({}, {})到({}, {})",
self.top_left.x, self.top_left.y,
self.bottom_right.x, self.bottom_right.y);
}
fn description(&self) -> String {
format!("矩形 ({}x{})", self.width(), self.height())
}
}
// 复杂形状:使用组合构建
struct CompositeShape {
shapes: Vec<Box<dyn Shape>>,
name: String,
}
impl CompositeShape {
fn new(name: &str) -> Self {
CompositeShape {
shapes: Vec::new(),
name: name.to_string(),
}
}
fn add_shape(&mut self, shape: Box<dyn Shape>) {
self.shapes.push(shape);
}
}
impl Shape for CompositeShape {
fn area(&self) -> f64 {
self.shapes.iter().map(|shape| shape.area()).sum()
}
fn perimeter(&self) -> f64 {
// 对于复合形状,周长的概念可能没有明确定义
// 这里简单返回所有形状周长之和
self.shapes.iter().map(|shape| shape.perimeter()).sum()
}
fn draw(&self) {
println!("绘制复合形状: {}", self.name);
for shape in &self.shapes {
shape.draw();
}
}
fn description(&self) -> String {
format!("复合形状: {} (包含{}个形状)", self.name, self.shapes.len())
}
}
// 使用示例
let circle = Circle::new(Point::new(0.0, 0.0), 5.0);
let rectangle = Rectangle::new(Point::new(1.0, 1.0), Point::new(4.0, 3.0));
let mut composite = CompositeShape::new("我的设计");
composite.add_shape(Box::new(circle));
composite.add_shape(Box::new(rectangle));
let shapes: Vec<&dyn Shape> = vec![
&composite,
];
for shape in shapes {
println!("{}", shape.description());
println!("面积: {:.2}", shape.area());
println!("周长: {:.2}", shape.perimeter());
shape.draw();
println!("---");
}
}
数据导向设计与面向对象设计
Rust鼓励数据导向设计,这与传统的面向对象设计有所不同。
fn data_oriented_design() {
println!("=== 数据导向设计与面向对象设计 ===");
// 面向对象方式:将数据和行为封装在对象中
struct OOPCharacter {
name: String,
health: u32,
position: (f64, f64),
velocity: (f64, f64),
}
impl OOPCharacter {
fn new(name: &str, health: u32, position: (f64, f64)) -> Self {
OOPCharacter {
name: name.to_string(),
health,
position,
velocity: (0.0, 0.0),
}
}
fn update(&mut self, delta_time: f64) {
self.position.0 += self.velocity.0 * delta_time;
self.position.1 += self.velocity.1 * delta_time;
}
fn take_damage(&mut self, damage: u32) {
self.health = self.health.saturating_sub(damage);
}
fn set_velocity(&mut self, velocity: (f64, f64)) {
self.velocity = velocity;
}
}
// 数据导向方式:将数据和行为分离
struct DODCharacterData {
name: String,
health: u32,
}
struct PhysicsData {
position: (f64, f64),
velocity: (f64, f64),
}
struct DODWorld {
character_data: Vec<DODCharacterData>,
physics_data: Vec<PhysicsData>,
}
impl DODWorld {
fn new() -> Self {
DODWorld {
character_data: Vec::new(),
physics_data: Vec::new(),
}
}
fn add_character(&mut self, name: &str, health: u32, position: (f64, f64)) {
self.character_data.push(DODCharacterData {
name: name.to_string(),
health,
});
self.physics_data.push(PhysicsData {
position,
velocity: (0.0, 0.0),
});
}
// 批量更新物理系统
fn update_physics(&mut self, delta_time: f64) {
for physics in &mut self.physics_data {
physics.position.0 += physics.velocity.0 * delta_time;
physics.position.1 += physics.velocity.1 * delta_time;
}
}
// 批量处理伤害
fn apply_damage_to_all(&mut self, damage: u32) {
for character in &mut self.character_data {
character.health = character.health.saturating_sub(damage);
}
}
// 查找特定角色
fn find_character_index(&self, name: &str) -> Option<usize> {
self.character_data.iter().position(|c| c.name == name)
}
fn set_character_velocity(&mut self, index: usize, velocity: (f64, f64)) {
if let Some(physics) = self.physics_data.get_mut(index) {
physics.velocity = velocity;
}
}
}
println!("面向对象方式:");
let mut oop_characters = vec![
OOPCharacter::new("英雄", 100, (0.0, 0.0)),
OOPCharacter::new("敌人", 50, (10.0, 5.0)),
];
for character in &mut oop_characters {
character.set_velocity((1.0, 0.5));
character.update(1.0);
character.take_damage(10);
}
println!("数据导向方式:");
let mut dod_world = DODWorld::new();
dod_world.add_character("英雄", 100, (0.0, 0.0));
dod_world.add_character("敌人", 50, (10.0, 5.0));
// 批量操作
dod_world.set_character_velocity(0, (1.0, 0.5));
dod_world.set_character_velocity(1, (0.5, 1.0));
dod_world.update_physics(1.0);
dod_world.apply_damage_to_all(10);
println!("两种方式各有优劣:");
println!("- 面向对象: 封装性好,易于理解单个对象的行为");
println!("- 数据导向: 缓存友好,适合批量处理,性能通常更好");
}
性能考虑与设计选择
在Rust中选择面向对象模式时需要考虑性能影响。
fn performance_considerations() {
println!("=== 性能考虑与设计选择 ===");
use std::time::Instant;
// 测试动态分发(trait对象)与静态分发(泛型)的性能差异
trait Calculator {
fn compute(&self, x: f64) -> f64;
}
struct SimpleCalculator;
impl Calculator for SimpleCalculator {
fn compute(&self, x: f64) -> f64 {
x * x + 2.0 * x + 1.0
}
}
struct ComplexCalculator;
impl Calculator for ComplexCalculator {
fn compute(&self, x: f64) -> f64 {
x.sin() * x.cos().exp()
}
}
// 使用trait对象(动态分发)
fn process_dynamic(calculators: &[Box<dyn Calculator>], data: &[f64]) -> Vec<f64> {
let mut results = Vec::with_capacity(data.len() * calculators.len());
for calc in calculators {
for &value in data {
results.push(calc.compute(value));
}
}
results
}
// 使用泛型(静态分发)
fn process_static<C: Calculator>(calculators: &[C], data: &[f64]) -> Vec<f64> {
let mut results = Vec::with_capacity(data.len() * calculators.len());
for calc in calculators {
for &value in data {
results.push(calc.compute(value));
}
}
results
}
// 性能测试
let data: Vec<f64> = (0..1000).map(|i| i as f64 * 0.01).collect();
let dynamic_calcs: Vec<Box<dyn Calculator>> = vec![
Box::new(SimpleCalculator),
Box::new(ComplexCalculator),
];
let static_calcs = [SimpleCalculator, ComplexCalculator];
// 测试动态分发
let start = Instant::now();
for _ in 0..1000 {
let _ = process_dynamic(&dynamic_calcs, &data);
}
let dynamic_duration = start.elapsed();
// 测试静态分发
let start = Instant::now();
for _ in 0..1000 {
let _ = process_static(&static_calcs, &data);
}
let static_duration = start.elapsed();
println!("动态分发耗时: {:?}", dynamic_duration);
println!("静态分发耗时: {:?}", static_duration);
println!("性能差异: {:.2}%",
(dynamic_duration.as_nanos() as f64 / static_duration.as_nanos() as f64 - 1.0) * 100.0);
println!("\n设计建议:");
println!("1. 在性能关键路径上优先使用静态分发(泛型)");
println!("2. 需要运行时多态时使用动态分发(trait对象)");
println!("3. 考虑使用枚举代替trait对象,如果类型集合是封闭的");
println!("4. 对于热点代码,避免不必要的堆分配和动态分发");
}
实战:混合风格的系统设计
让我们设计一个实际的系统,展示如何在Rust中结合面向对象和其他范式。
fn hybrid_system_design() {
println!("=== 混合风格的系统设计 ===");
// 设计一个简单的游戏实体系统,结合面向对象和数据导向元素
// 组件定义(数据导向)
#[derive(Debug, Clone)]
struct Position {
x: f64,
y: f64,
}
#[derive(Debug, Clone)]
struct Velocity {
dx: f64,
dy: f64,
}
#[derive(Debug, Clone)]
struct Health {
current: u32,
maximum: u32,
}
#[derive(Debug, Clone)]
struct Name {
value: String,
}
// 实体(面向对象风格)
struct Entity {
id: u32,
components: std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any>>,
}
impl Entity {
fn new(id: u32) -> Self {
Entity {
id,
components: std::collections::HashMap::new(),
}
}
fn add_component<T: 'static>(&mut self, component: T) {
self.components.insert(std::any::TypeId::of::<T>(), Box::new(component));
}
fn get_component<T: 'static>(&self) -> Option<&T> {
self.components
.get(&std::any::TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_ref::<T>())
}
fn get_component_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.components
.get_mut(&std::any::TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_mut::<T>())
}
fn has_component<T: 'static>(&self) -> bool {
self.components.contains_key(&std::any::TypeId::of::<T>())
}
}
// 系统(数据导向风格)
struct PhysicsSystem;
impl PhysicsSystem {
fn update(&self, entities: &mut [Entity], delta_time: f64) {
for entity in entities {
if let (Some(position), Some(velocity)) = (
entity.get_component_mut::<Position>(),
entity.get_component::<Velocity>(),
) {
position.x += velocity.dx * delta_time;
position.y += velocity.dy * delta_time;
}
}
}
}
struct RenderSystem;
impl RenderSystem {
fn render(&self, entities: &[Entity]) {
for entity in entities {
if let (Some(position), Some(name)) = (
entity.get_component::<Position>(),
entity.get_component::<Name>(),
) {
println!("渲染 {} 在位置 ({}, {})", name.value, position.x, position.y);
}
}
}
}
// 使用示例
let mut entities = vec![
{
let mut entity = Entity::new(1);
entity.add_component(Name { value: "玩家".to_string() });
entity.add_component(Position { x: 0.0, y: 0.0 });
entity.add_component(Velocity { dx: 1.0, dy: 0.5 });
entity.add_component(Health { current: 100, maximum: 100 });
entity
},
{
let mut entity = Entity::new(2);
entity.add_component(Name { value: "敌人".to_string() });
entity.add_component(Position { x: 5.0, y: 3.0 });
entity.add_component(Velocity { dx: -0.5, dy: 0.2 });
entity.add_component(Health { current: 50, maximum: 50 });
entity
},
];
let physics_system = PhysicsSystem;
let render_system = RenderSystem;
println!("初始状态:");
render_system.render(&entities);
println!("\n更新物理:");
physics_system.update(&mut entities, 1.0);
render_system.render(&entities);
println!("\n再次更新物理:");
physics_system.update(&mut entities, 1.0);
render_system.render(&entities);
// 检查特定组件
println!("\n组件检查:");
for entity in &entities {
if let Some(health) = entity.get_component::<Health>() {
println!("实体 {} 生命值: {}/{}",
entity.id, health.current, health.maximum);
}
}
}
fn main() {
encapsulation_demo();
composition_demo();
method_syntax_demo();
trait_objects_basics();
advanced_trait_objects_demo();
trait_object_limitations();
factory_pattern_demo();
observer_pattern_demo();
strategy_pattern_demo();
composition_vs_inheritance();
data_oriented_design();
performance_considerations();
hybrid_system_design();
}
总结
Rust以独特的方式支持面向对象编程范式,通过结构体、枚举、trait和模式匹配等特性实现了封装、多态和代码复用。本章深入探讨了:
- 面向对象语言特性:封装、组合、方法语法等基本概念
- Trait对象:动态多态的实现方式和应用场景
- 设计模式:工厂模式、观察者模式、策略模式等在Rust中的实现
- 风格权衡:Rust哲学与面向对象编程的融合,性能考虑和设计选择
Rust不强制使用特定的编程范式,而是提供了灵活的工具集,让开发者能够根据具体需求选择最合适的模式。理解如何在Rust中有效地使用面向对象概念,同时充分利用Rust的强类型系统、所有权模型和零成本抽象,是编写高质量Rust代码的关键。
在下一章中,我们将探讨Rust中的模式和匹配,学习如何利用Rust强大的模式匹配功能来编写更简洁、更安全的代码。
更多推荐



所有评论(0)