Ruby 面向对象
面向对象编程(OOP)是Ruby的核心特性之一。在Ruby中,一切都是对象,这使得面向对象编程变得非常自然和强大。Ruby提供了丰富的面向对象特性,包括类、对象、继承、模块、封装和多态等。本章将详细介绍Ruby中面向对象编程的各种概念和最佳实践。
🎯 面向对象基础
什么是面向对象编程
面向对象编程是一种编程范式,它使用"对象"来设计软件。对象是数据和操作数据的方法的组合。Ruby中的面向对象特性包括:
- 类(Class):对象的模板或蓝图
- 对象(Object):类的实例
- 封装(Encapsulation):隐藏内部实现细节
- 继承(Inheritance):从现有类创建新类
- 多态(Polymorphism):同一接口的不同实现
- 模块(Module):代码重用和命名空间
ruby
# 基本类定义
class Person
# 构造方法
def initialize(name, age)
@name = name # 实例变量
@age = age
end
# 实例方法
def introduce
"我是#{@name},今年#{@age}岁"
end
# getter方法
def name
@name
end
# setter方法
def name=(new_name)
@name = new_name
end
end
# 创建对象
person = Person.new("张三", 25)
puts person.introduce # 我是张三,今年25岁
# 使用getter和setter
puts person.name # 张三
person.name = "李四"
puts person.name # 李四类和对象基础
ruby
# 类定义
class Car
# 类变量
@@total_cars = 0
# 构造方法
def initialize(brand, model)
@brand = brand
@model = model
@mileage = 0
@@total_cars += 1
end
# 实例方法
def drive(distance)
@mileage += distance
"行驶了#{distance}公里,总里程#{@mileage}公里"
end
# 类方法
def self.total_cars
@@total_cars
end
# 实例变量访问器
attr_reader :brand, :model, :mileage
attr_writer :mileage
attr_accessor :color # 自动生成getter和setter
end
# 创建对象
car1 = Car.new("丰田", "卡罗拉")
car2 = Car.new("本田", "雅阁")
# 使用对象
puts car1.brand # 丰田
puts car1.drive(100) # 行驶了100公里,总里程100公里
car1.color = "红色"
puts car1.color # 红色
# 调用类方法
puts Car.total_cars # 2🏗️ 类的定义和方法
构造方法和初始化
ruby
class Student
# 使用attr_accessor自动生成访问器
attr_accessor :name, :age, :grade
# 构造方法
def initialize(name, age, grade = "未知")
@name = name
@age = age
@grade = grade
@courses = [] # 默认值
end
# 带默认参数的方法
def enroll(course, semester = "秋季")
@courses << { name: course, semester: semester }
end
# 带可变参数的方法
def add_grades(*grades)
@grades = grades.flatten
end
# 带关键字参数的方法
def update_info(name: nil, age: nil, grade: nil)
@name = name if name
@age = age if age
@grade = grade if grade
end
# 带块参数的方法
def with_logging
puts "开始操作"
result = yield if block_given?
puts "操作完成"
result
end
# 实例方法
def info
"姓名: #{@name}, 年龄: #{@age}, 年级: #{@grade}"
end
def courses
@courses.map { |course| "#{course[:name]} (#{course[:semester]})" }
end
end
# 使用Student类
student = Student.new("张三", 20, "大二")
student.enroll("数学")
student.enroll("英语", "春季")
student.add_grades(85, 92, 78)
puts student.info
puts student.courses.inspect
student.update_info(age: 21, grade: "大三")
puts student.info
student.with_logging { puts "更新学生信息" }访问控制
ruby
class BankAccount
# 公共方法
def initialize(account_number, initial_balance = 0)
@account_number = account_number
@balance = initial_balance
@transaction_history = []
end
# 公共方法 - 任何人都可以调用
def account_number
@account_number
end
def balance
@balance
end
def deposit(amount)
return false if amount <= 0
@balance += amount
log_transaction("存款", amount)
true
end
def withdraw(amount)
return false if amount <= 0 || amount > @balance
@balance -= amount
log_transaction("取款", -amount)
true
end
# 受保护方法 - 只有类及其子类可以调用
protected
def log_transaction(type, amount)
transaction = {
type: type,
amount: amount,
balance: @balance,
timestamp: Time.now
}
@transaction_history << transaction
end
# 私有方法 - 只有类内部可以调用
private
def validate_amount(amount)
amount > 0 && amount.is_a?(Numeric)
end
def generate_statement
"账户: #{@account_number}, 余额: #{@balance}"
end
# 私有方法也可以被公共方法调用
public
def print_statement
generate_statement # 可以调用私有方法
end
end
# 使用银行账户
account = BankAccount.new("123456789", 1000)
puts account.account_number # 123456789
puts account.balance # 1000
account.deposit(500)
puts account.balance # 1500
account.withdraw(200)
puts account.balance # 1300
puts account.print_statement # 账户: 123456789, 余额: 1300
# account.log_transaction("测试", 100) # 错误:受保护方法
# account.generate_statement # 错误:私有方法🔗 继承和多态
类继承
ruby
# 基类
class Animal
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
# 虚方法(子类应该重写)
def speak
raise NotImplementedError, "子类必须实现speak方法"
end
# 公共方法
def info
"#{@name}是一只#{@age}岁的动物"
end
# 私有方法
private
def species
"动物"
end
end
# 继承基类
class Dog < Animal
attr_accessor :breed
def initialize(name, age, breed)
super(name, age) # 调用父类构造方法
@breed = breed
end
# 重写父类方法
def speak
"#{@name}汪汪叫"
end
# 新增方法
def fetch
"#{@name}去捡球"
end
# 重写父类方法并调用父类方法
def info
super + ",品种是#{@breed}"
end
end
class Cat < Animal
attr_accessor :color
def initialize(name, age, color)
super(name, age)
@color = color
end
# 重写父类方法
def speak
"#{@name}喵喵叫"
end
# 新增方法
def climb
"#{@name}爬树"
end
end
# 使用继承
dog = Dog.new("旺财", 3, "金毛")
cat = Cat.new("咪咪", 2, "橘色")
puts dog.info # 旺财是一只3岁的动物,品种是金毛
puts dog.speak # 旺财汪汪叫
puts dog.fetch # 旺财去捡球
puts cat.info # 咪咪是一只2岁的动物
puts cat.speak # 咪咪喵喵叫
puts cat.climb # 咪咪爬树方法查找和super关键字
ruby
class A
def method1
"A#method1"
end
def method2
"A#method2"
end
end
class B < A
def method1
"B#method1 (#{super})" # 调用父类方法
end
def method2
super # 直接调用父类方法
end
def method3
"B#method3"
end
end
class C < B
def method1
"C#method1 (#{super})" # 调用B#method1,B又调用A#method1
end
end
# 方法查找链
a = A.new
b = B.new
c = C.new
puts a.method1 # A#method1
puts b.method1 # B#method1 (A#method1)
puts c.method1 # C#method1 (B#method1 (A#method1))
puts a.method2 # A#method2
puts b.method2 # A#method2
puts c.method2 # A#method2
puts b.method3 # B#method3
puts c.method3 # B#method3📦 模块和混入
模块定义和使用
ruby
# 定义模块
module Drawable
def draw
"绘制图形"
end
def erase
"擦除图形"
end
# 模块常量
PI = 3.14159
end
module Movable
def move(x, y)
"移动到坐标(#{x}, #{y})"
end
def rotate(angle)
"旋转#{angle}度"
end
end
# 使用模块(包含)
class Shape
include Drawable
include Movable
attr_accessor :x, :y
def initialize(x = 0, y = 0)
@x, @y = x, y
end
def position
"位置: (#{@x}, #{@y})"
end
end
# 使用扩展模块(extend)
class Circle < Shape
extend Drawable # 作为类方法扩展
attr_accessor :radius
def initialize(x, y, radius)
super(x, y)
@radius = radius
end
def area
Math::PI * @radius ** 2
end
# 类方法(通过extend获得)
def self.draw_circle
"绘制圆形"
end
end
# 使用模块
shape = Shape.new(10, 20)
puts shape.draw # 绘制图形
puts shape.move(30, 40) # 移动到坐标(30, 40)
puts shape.position # 位置: (30, 40)
circle = Circle.new(0, 0, 5)
puts circle.area # 78.53981633974483
puts circle.position # 位置: (0, 0)
# 调用类方法
puts Circle.draw # 绘制图形(来自Drawable模块)
puts Circle.draw_circle # 绘制圆形模块混入和命名空间
ruby
# 命名空间模块
module Graphics
class Point
attr_accessor :x, :y
def initialize(x, y)
@x, @y = x, y
end
def distance(other)
Math.sqrt((@x - other.x) ** 2 + (@y - other.y) ** 2)
end
end
class Line
attr_accessor :start_point, :end_point
def initialize(start_point, end_point)
@start_point = start_point
@end_point = end_point
end
def length
@start_point.distance(@end_point)
end
end
end
# 使用命名空间
point1 = Graphics::Point.new(0, 0)
point2 = Graphics::Point.new(3, 4)
line = Graphics::Line.new(point1, point2)
puts point1.distance(point2) # 5.0
puts line.length # 5.0
# 混入模块提供共享功能
module ComparableByAge
def <=>(other)
@age <=> other.age
end
def >(other)
(@age > other.age)
end
def <(other)
(@age < other.age)
end
def ==(other)
@age == other.age
end
end
class Person
include Comparable
include ComparableByAge
attr_accessor :name, :age
def initialize(name, age)
@name, @age = name, age
end
def to_s
"#{@name}(#{@age}岁)"
end
end
# 使用比较功能
people = [
Person.new("张三", 25),
Person.new("李四", 30),
Person.new("王五", 20)
]
puts people.sort.map(&:to_s) # 王五(20岁), 张三(25岁), 李四(30岁)
puts people.max.to_s # 李四(30岁)
puts people.min.to_s # 王五(20岁)🎯 面向对象实践示例
用户管理系统
ruby
# 基础用户类
class User
attr_accessor :username, :email, :created_at
attr_reader :id
@@next_id = 1
@@users = {}
def initialize(username, email)
@id = @@next_id
@@next_id += 1
@username = username
@email = email
@created_at = Time.now
@active = true
@@users[@id] = self
end
def activate
@active = true
end
def deactivate
@active = false
end
def active?
@active
end
def info
"用户ID: #{@id}, 用户名: #{@username}, 邮箱: #{@email}, 状态: #{@active ? '活跃' : '非活跃'}"
end
# 类方法
def self.find(id)
@@users[id]
end
def self.all
@@users.values
end
def self.active_users
@@users.values.select(&:active?)
end
def self.deactivated_users
@@users.values.reject(&:active?)
end
end
# 管理员用户类
class AdminUser < User
attr_accessor :permissions
def initialize(username, email, permissions = [])
super(username, email)
@permissions = permissions
end
def grant_permission(permission)
@permissions << permission unless @permissions.include?(permission)
end
def revoke_permission(permission)
@permissions.delete(permission)
end
def can?(permission)
@permissions.include?(permission)
end
# 重写info方法
def info
super + ", 权限: #{@permissions.join(', ')}"
end
# 管理员特有方法
def deactivate_user(user_id)
user = User.find(user_id)
user&.deactivate
end
def list_all_users
User.all.map(&:info)
end
end
# 使用用户管理系统
# 创建普通用户
user1 = User.new("zhangsan", "zhangsan@example.com")
user2 = User.new("lisi", "lisi@example.com")
# 创建管理员用户
admin = AdminUser.new("admin", "admin@example.com", ["manage_users", "view_reports"])
# 管理员操作
admin.grant_permission("delete_users")
puts admin.info
# 停用用户
admin.deactivate_user(user2.id)
puts user2.active? # false
# 查看所有用户
puts "所有用户:"
User.all.each { |user| puts user.info }
puts "活跃用户数: #{User.active_users.length}"
puts "非活跃用户数: #{User.deactivated_users.length}"银行系统
ruby
# 银行账户基类
class BankAccount
attr_reader :account_number, :balance, :owner
@@next_account_number = 100000
@@accounts = {}
def initialize(owner, initial_balance = 0)
@account_number = @@next_account_number
@@next_account_number += 1
@owner = owner
@balance = initial_balance
@transactions = []
@@accounts[@account_number] = self
log_transaction("开户", initial_balance)
end
def deposit(amount)
return false if amount <= 0
@balance += amount
log_transaction("存款", amount)
true
end
def withdraw(amount)
return false if amount <= 0 || amount > @balance
@balance -= amount
log_transaction("取款", -amount)
true
end
def transfer_to(other_account, amount)
return false if amount <= 0 || amount > @balance
return false unless other_account.is_a?(BankAccount)
withdraw(amount)
other_account.deposit(amount)
log_transaction("转账到#{other_account.account_number}", -amount)
other_account.log_transaction("从#{@account_number}转账", amount)
true
end
def transaction_history
@transactions.dup
end
def self.find(account_number)
@@accounts[account_number]
end
def self.all_accounts
@@accounts.values
end
def self.total_balance
@@accounts.values.sum(&:balance)
end
protected
def log_transaction(type, amount)
transaction = {
type: type,
amount: amount,
balance: @balance,
timestamp: Time.now
}
@transactions << transaction
end
end
# 储蓄账户
class SavingsAccount < BankAccount
def initialize(owner, initial_balance = 0, interest_rate = 0.02)
super(owner, initial_balance)
@interest_rate = interest_rate
@last_interest_date = Date.today
end
def add_interest
today = Date.today
return if today <= @last_interest_date
interest = @balance * @interest_rate
deposit(interest)
log_transaction("利息", interest)
@last_interest_date = today
end
def info
"储蓄账户 #{@account_number}, 余额: #{@balance}, 利率: #{@interest_rate * 100}%"
end
end
# 支票账户
class CheckingAccount < BankAccount
def initialize(owner, initial_balance = 0, overdraft_limit = 0)
super(owner, initial_balance)
@overdraft_limit = overdraft_limit
end
# 重写取款方法以支持透支
def withdraw(amount)
return false if amount <= 0
return false if (@balance + @overdraft_limit) < amount
@balance -= amount
log_transaction("取款", -amount)
true
end
def info
"支票账户 #{@account_number}, 余额: #{@balance}, 透支额度: #{@overdraft_limit}"
end
end
# 银行管理系统
class Bank
def initialize(name)
@name = name
@accounts = []
end
def open_savings_account(owner, initial_balance = 0, interest_rate = 0.02)
account = SavingsAccount.new(owner, initial_balance, interest_rate)
@accounts << account
account
end
def open_checking_account(owner, initial_balance = 0, overdraft_limit = 0)
account = CheckingAccount.new(owner, initial_balance, overdraft_limit)
@accounts << account
account
end
def find_account(account_number)
@accounts.find { |account| account.account_number == account_number }
end
def total_assets
@accounts.sum(&:balance)
end
def accounts_info
@accounts.map(&:info)
end
end
# 使用银行系统
bank = Bank.new("中国银行")
# 开设账户
savings = bank.open_savings_account("张三", 10000, 0.03)
checking = bank.open_checking_account("李四", 5000, 1000)
# 操作账户
savings.deposit(2000)
checking.withdraw(6000) # 使用透支额度
# 转账
savings.transfer_to(checking, 3000)
# 查看账户信息
puts savings.info
puts checking.info
puts "银行总资产: #{bank.total_assets}"
# 查看交易历史
puts "储蓄账户交易历史:"
savings.transaction_history.each do |transaction|
puts " #{transaction[:timestamp].strftime('%Y-%m-%d %H:%M')} - #{transaction[:type]}: #{transaction[:amount]}, 余额: #{transaction[:balance]}"
end📊 面向对象设计原则
SOLID原则应用
ruby
# S - 单一职责原则 (Single Responsibility Principle)
# 每个类应该只有一个改变的理由
# 不好的设计
class User
def initialize(name, email)
@name = name
@email = email
end
# 用户数据管理
def save
# 保存到数据库
end
def validate
# 验证用户数据
end
# 邮件发送
def send_welcome_email
# 发送欢迎邮件
end
# 日志记录
def log_activity(activity)
# 记录用户活动
end
end
# 好的设计
class User
attr_accessor :name, :email
def initialize(name, email)
@name = name
@email = email
end
end
class UserDatabase
def self.save(user)
# 保存用户到数据库
end
def self.find(id)
# 从数据库查找用户
end
end
class UserValidator
def self.validate(user)
# 验证用户数据
end
end
class EmailService
def self.send_welcome_email(user)
# 发送欢迎邮件
end
end
class ActivityLogger
def self.log(user, activity)
# 记录用户活动
end
end
# O - 开放-封闭原则 (Open/Closed Principle)
# 对扩展开放,对修改封闭
# 基础报表类
class Report
def generate
data = fetch_data
format_data(data)
end
private
def fetch_data
# 获取数据的通用逻辑
[]
end
def format_data(data)
# 默认格式化
data.join("\n")
end
end
# 扩展报表类而不修改原有代码
class PDFReport < Report
def format_data(data)
# PDF格式化
"PDF: #{data.join(', ')}"
end
end
class ExcelReport < Report
def format_data(data)
# Excel格式化
"Excel: #{data.join("\t")}"
end
end
# L - 里氏替换原则 (Liskov Substitution Principle)
# 子类应该能够替换父类
class Bird
def fly
"鸟在飞"
end
end
class Sparrow < Bird
def fly
"麻雀在飞"
end
end
class Ostrich < Bird
# 鸵鸟不会飞,违反了里氏替换原则
def fly
raise "鸵鸟不会飞"
end
end
# 改进设计
class Bird
# 鸟类的基础功能
end
class FlyingBird < Bird
def fly
"鸟在飞"
end
end
class Sparrow < FlyingBird
def fly
"麻雀在飞"
end
end
class Ostrich < Bird
def run
"鸵鸟在跑"
end
end
# I - 接口隔离原则 (Interface Segregation Principle)
# 客户端不应该依赖它不需要的接口
# 不好的设计
module Worker
def work
# 工作
end
def eat
# 吃饭
end
def sleep
# 睡觉
end
end
class HumanWorker
include Worker
def work
"人类在工作"
end
def eat
"人类在吃饭"
end
def sleep
"人类在睡觉"
end
end
class RobotWorker
include Worker
def work
"机器人在工作"
end
def eat
# 机器人不需要吃饭
raise "机器人不需要吃饭"
end
def sleep
# 机器人不需要睡觉
raise "机器人不需要睡觉"
end
end
# 好的设计
module Workable
def work
# 工作
end
end
module Eatable
def eat
# 吃饭
end
end
module Sleepable
def sleep
# 睡觉
end
end
class HumanWorker
include Workable
include Eatable
include Sleepable
def work
"人类在工作"
end
def eat
"人类在吃饭"
end
def sleep
"人类在睡觉"
end
end
class RobotWorker
include Workable
def work
"机器人在工作"
end
end
# D - 依赖倒置原则 (Dependency Inversion Principle)
# 依赖于抽象,而不是具体实现
# 不好的设计
class EmailNotifier
def notify(message)
# 发送邮件通知
puts "发送邮件: #{message}"
end
end
class UserService
def initialize
@notifier = EmailNotifier.new
end
def create_user(user_data)
# 创建用户逻辑
@notifier.notify("用户已创建")
end
end
# 好的设计
class Notifier
def notify(message)
raise NotImplementedError
end
end
class EmailNotifier < Notifier
def notify(message)
puts "发送邮件: #{message}"
end
end
class SMSNotifier < Notifier
def notify(message)
puts "发送短信: #{message}"
end
end
class UserService
def initialize(notifier)
@notifier = notifier
end
def create_user(user_data)
# 创建用户逻辑
@notifier.notify("用户已创建")
end
end
# 使用
email_notifier = EmailNotifier.new
sms_notifier = SMSNotifier.new
user_service1 = UserService.new(email_notifier)
user_service2 = UserService.new(sms_notifier)🛡️ 面向对象最佳实践
1. 设计模式应用
ruby
# 单例模式
class Logger
@@instance = nil
private_class_method :new
def self.instance
@@instance ||= new
end
def log(message)
puts "[#{Time.now}] #{message}"
end
private
def initialize
# 私有构造方法
end
end
# 使用单例
logger1 = Logger.instance
logger2 = Logger.instance
puts logger1.equal?(logger2) # true
logger1.log("第一条日志")
logger2.log("第二条日志")
# 工厂模式
class AnimalFactory
def self.create_animal(type, name)
case type.downcase
when 'dog'
Dog.new(name)
when 'cat'
Cat.new(name)
when 'bird'
Bird.new(name)
else
raise "未知的动物类型: #{type}"
end
end
end
# 使用工厂
dog = AnimalFactory.create_animal('dog', '旺财')
cat = AnimalFactory.create_animal('cat', '咪咪')
# 观察者模式
class Subject
def initialize
@observers = []
end
def add_observer(observer)
@observers << observer
end
def remove_observer(observer)
@observers.delete(observer)
end
def notify_observers
@observers.each { |observer| observer.update(self) }
end
end
class TemperatureSensor < Subject
attr_reader :temperature
def initialize
super
@temperature = 0
end
def temperature=(temp)
@temperature = temp
notify_observers if temp > 30
end
end
class TemperatureDisplay
def update(subject)
puts "警告: 温度过高! 当前温度: #{subject.temperature}°C"
end
end
class TemperatureLogger
def update(subject)
puts "记录温度: #{subject.temperature}°C at #{Time.now}"
end
end
# 使用观察者模式
sensor = TemperatureSensor.new
display = TemperatureDisplay.new
logger = TemperatureLogger.new
sensor.add_observer(display)
sensor.add_observer(logger)
sensor.temperature = 25 # 无警告
sensor.temperature = 35 # 触发警告2. 代码组织和结构
ruby
# 模块化设计
module Payment
class Base
def initialize(amount)
@amount = amount
end
def process
raise NotImplementedError, "子类必须实现process方法"
end
end
class CreditCard < Base
def initialize(amount, card_number, cvv)
super(amount)
@card_number = card_number
@cvv = cvv
end
def process
"处理信用卡支付: #{@amount}元"
end
end
class Alipay < Base
def initialize(amount, account)
super(amount)
@account = account
end
def process
"处理支付宝支付: #{@amount}元"
end
end
class WeChatPay < Base
def initialize(amount, openid)
super(amount)
@openid = openid
end
def process
"处理微信支付: #{@amount}元"
end
end
end
# 使用支付模块
credit_card = Payment::CreditCard.new(100, "1234****5678", "123")
alipay = Payment::Alipay.new(100, "user@example.com")
wechat = Payment::WeChatPay.new(100, "openid123")
puts credit_card.process
puts alipay.process
puts wechat.process
# 策略模式
class PaymentProcessor
def initialize(payment_strategy)
@payment_strategy = payment_strategy
end
def process_payment(amount)
@payment_strategy.process(amount)
end
end
# 使用策略模式
processor1 = PaymentProcessor.new(Payment::CreditCard.new(100, "1234****5678", "123"))
processor2 = PaymentProcessor.new(Payment::Alipay.new(100, "user@example.com"))
puts processor1.process_payment(100)
puts processor2.process_payment(100)3. 测试友好的设计
ruby
# 依赖注入提高可测试性
class OrderService
def initialize(payment_gateway = nil, inventory_service = nil)
@payment_gateway = payment_gateway || PaymentGateway.new
@inventory_service = inventory_service || InventoryService.new
end
def process_order(order)
# 检查库存
return false unless @inventory_service.check_stock(order.items)
# 处理支付
payment_result = @payment_gateway.charge(order.total_amount)
return false unless payment_result.success?
# 更新库存
@inventory_service.update_stock(order.items)
true
end
end
# 模拟对象用于测试
class MockPaymentGateway
def charge(amount)
OpenStruct.new(success?: true)
end
end
class MockInventoryService
def check_stock(items)
true
end
def update_stock(items)
# 模拟更新库存
end
end
# 测试代码
# order_service = OrderService.new(MockPaymentGateway.new, MockInventoryService.new)
# result = order_service.process_order(order)
# assert(result, "订单处理应该成功")📚 下一步学习
掌握了Ruby面向对象编程后,建议继续学习:
- Ruby 数据库访问 - 学习数据库操作
- Ruby 网络编程 - 了解Socket编程
- Ruby 多线程 - 掌握并发编程
- Ruby Web服务 - 学习Web服务开发
继续您的Ruby学习之旅吧!