Ruby 方法
方法是组织代码的基本单元,用于封装可重用的代码逻辑。Ruby中的方法功能强大且灵活,支持多种参数形式和高级特性。本章将详细介绍Ruby中方法的定义、调用和高级用法。
🎯 方法基础
方法定义
ruby
# 基本方法定义
def greet
puts "Hello, World!"
end
# 带参数的方法
def greet_person(name)
puts "Hello, #{name}!"
end
# 带默认参数的方法
def greet_with_title(name, title = "先生/女士")
puts "Hello, #{title} #{name}!"
end
# 调用方法
greet # Hello, World!
greet_person("张三") # Hello, 张三!
greet_with_title("李四") # Hello, 先生/女士 李四!
greet_with_title("王五", "经理") # Hello, 经理 王五!方法返回值
ruby
# 方法自动返回最后一个表达式的值
def add(a, b)
a + b # 自动返回
end
# 显式返回
def multiply(a, b)
return a * b
end
# 多值返回
def get_name_and_age
["张三", 25]
end
# 使用并行赋值接收多值返回
name, age = get_name_and_age
puts "姓名: #{name}, 年龄: #{age}" # 姓名: 张三, 年龄: 25
# 条件返回
def divide(a, b)
return nil if b == 0
a / b
end
puts divide(10, 2) # 5
puts divide(10, 0) # nil📦 参数类型
位置参数
ruby
# 必需的位置参数
def create_user(name, age, email)
{name: name, age: age, email: email}
end
user = create_user("张三", 25, "zhangsan@example.com")
# 可选的位置参数(默认值)
def greet_person(name, greeting = "Hello", punctuation = "!")
"#{greeting}, #{name}#{punctuation}"
end
puts greet_person("张三") # Hello, 张三!
puts greet_person("李四", "你好") # 你好, 李四!
puts greet_person("王五", "Bonjour", ".") # Bonjour, 王五.关键字参数
ruby
# 关键字参数(Ruby 2.0+)
def create_user(name:, age:, email: nil)
{name: name, age: age, email: email}
end
# 调用时必须使用关键字
user = create_user(name: "张三", age: 25)
user = create_user(age: 30, name: "李四", email: "lisi@example.com")
# 带默认值的关键字参数
def greet_person(name:, greeting: "Hello", punctuation: "!")
"#{greeting}, #{name}#{punctuation}"
end
puts greet_person(name: "张三") # Hello, 张三!可变参数
ruby
# 可变参数(splat operator)
def sum(*numbers)
numbers.reduce(0) { |total, n| total + n }
end
puts sum(1, 2, 3) # 6
puts sum(1, 2, 3, 4, 5) # 15
puts sum() # 0
# 混合使用位置参数和可变参数
def greet_and_list(name, *items)
puts "Hello, #{name}!"
puts "你的物品:"
items.each { |item| puts "- #{item}" }
end
greet_and_list("张三", "苹果", "香蕉", "橙子")
# Hello, 张三!
# 你的物品:
# - 苹果
# - 香蕉
# - 橙子关键字可变参数
ruby
# 关键字可变参数(double splat operator)
def configure(**options)
options.each { |key, value| puts "#{key}: #{value}" }
end
configure(host: "localhost", port: 3000, debug: true)
# host: localhost
# port: 3000
# debug: true
# 混合使用多种参数类型
def complex_method(required, optional = "default", *args, **kwargs)
puts "必需参数: #{required}"
puts "可选参数: #{optional}"
puts "可变参数: #{args}"
puts "关键字参数: #{kwargs}"
end
complex_method("必需", "可选", "额外1", "额外2", key1: "值1", key2: "值2")
# 必需参数: 必需
# 可选参数: 可选
# 可变参数: ["额外1", "额外2"]
# 关键字参数: {:key1=>"值1", :key2=>"值2"}参数解构
ruby
# 数组参数解构
def process_coordinates((x, y))
puts "X坐标: #{x}, Y坐标: #{y}"
end
point = [10, 20]
process_coordinates(point) # X坐标: 10, Y坐标: 20
# 哈希参数解构
def process_person(name:, age:)
puts "姓名: #{name}, 年龄: #{age}"
end
person = {name: "张三", age: 25}
process_person(**person) # 姓名: 张三, 年龄: 25🔧 方法高级特性
方法别名
ruby
# 为方法创建别名
def original_method
"原始方法"
end
alias_method :aliased_method, :original_method
puts original_method # 原始方法
puts aliased_method # 原始方法
# 在类中使用别名
class Calculator
def add(a, b)
a + b
end
alias_method :plus, :add
end
calc = Calculator.new
puts calc.add(2, 3) # 5
puts calc.plus(2, 3) # 5方法可见性
ruby
class MyClass
def public_method
"公共方法"
end
private
def private_method
"私有方法"
end
protected
def protected_method
"受保护方法"
end
public
def call_private
private_method # 可以在类内部调用私有方法
end
end
obj = MyClass.new
puts obj.public_method # 公共方法
puts obj.call_private # 私有方法
# obj.private_method # 错误: private method
# obj.protected_method # 错误: protected method方法缺失处理
ruby
class FlexibleObject
def initialize
@data = {}
end
# 处理未定义的方法调用
def method_missing(method_name, *args, &block)
if method_name.to_s.end_with?('=')
# setter方法
key = method_name.to_s.chomp('=').to_sym
@data[key] = args.first
else
# getter方法
@data[method_name]
end
end
# 告诉Ruby哪些方法是可响应的
def respond_to_missing?(method_name, include_private = false)
true
end
end
obj = FlexibleObject.new
obj.name = "张三"
obj.age = 25
puts obj.name # 张三
puts obj.age # 25🔄 代码块和Proc
方法接受代码块
ruby
# 方法接受代码块参数
def with_logging
puts "开始执行"
yield if block_given?
puts "执行完成"
end
with_logging { puts "执行具体操作" }
# 开始执行
# 执行具体操作
# 执行完成
# 显式接受块参数
def with_timer(&block)
start_time = Time.now
result = block.call
end_time = Time.now
puts "执行时间: #{end_time - start_time}秒"
result
end
result = with_timer { sleep(1); "完成" }
puts resultProc和Lambda
ruby
# 创建Proc
my_proc = Proc.new { |x| x * 2 }
puts my_proc.call(5) # 10
# 创建Lambda
my_lambda = lambda { |x| x * 2 }
puts my_lambda.call(5) # 10
# 简写Lambda语法
my_lambda2 = ->(x) { x * 2 }
puts my_lambda2.call(5) # 10
# Proc和Lambda的区别
def test_return
proc = Proc.new { return "Proc返回" }
lambda = -> { return "Lambda返回" }
proc.call # 会从test_return方法返回
lambda.call # 只从lambda内部返回
"方法结束"
end
# puts test_return # Proc返回方法传递代码块
ruby
# 将块传递给其他方法
def process_array(array, &block)
array.map(&block)
end
numbers = [1, 2, 3, 4, 5]
squared = process_array(numbers) { |n| n * n }
puts squared.inspect # [1, 4, 9, 16, 25]
# 使用符号快捷方式
words = ["hello", "world", "ruby"]
upcased = words.map(&:upcase)
puts upcased.inspect # ["HELLO", "WORLD", "RUBY"]🎯 方法实践示例
配置构建器
ruby
class ConfigBuilder
def initialize
@config = {}
end
def method_missing(method_name, *args, &block)
if method_name.to_s.end_with?('=')
key = method_name.to_s.chomp('=').to_sym
@config[key] = args.first
elsif block_given?
@config[method_name] = block
elsif args.length == 1
@config[method_name] = args.first
elsif args.length > 1
@config[method_name] = args
else
@config[method_name]
end
end
def respond_to_missing?(method_name, include_private = false)
true
end
def build
@config.dup
end
def configure(&block)
instance_eval(&block) if block_given?
self
end
end
# 使用配置构建器
config = ConfigBuilder.new.configure do
database_url "postgresql://localhost/myapp"
port 3000
debug true
after_initialize { puts "初始化完成" }
end.build
puts config
# {:database_url=>"postgresql://localhost/myapp", :port=>3000, :debug=>true, :after_initialize=>#<Proc:0x000000010d0a8f80>}链式调用方法
ruby
class QueryBuilder
def initialize
@query = {}
end
def select(*fields)
@query[:select] = fields
self # 返回self支持链式调用
end
def from(table)
@query[:from] = table
self
end
def where(condition)
@query[:where] = condition
self
end
def limit(count)
@query[:limit] = count
self
end
def order_by(field, direction = :asc)
@query[:order] = {field => direction}
self
end
def build
query_string = "SELECT "
query_string += @query[:select] ? @query[:select].join(", ") : "*"
query_string += " FROM #{@query[:from]}" if @query[:from]
query_string += " WHERE #{@query[:where]}" if @query[:where]
query_string += " ORDER BY #{@query[:order].keys.first} #{@query[:order].values.first}" if @query[:order]
query_string += " LIMIT #{@query[:limit]}" if @query[:limit]
query_string
end
end
# 使用链式调用
query = QueryBuilder.new
.select(:name, :age)
.from(:users)
.where("age > 18")
.order_by(:name, :desc)
.limit(10)
.build
puts query
# SELECT name, age FROM users WHERE age > 18 ORDER BY name desc LIMIT 10装饰器模式方法
ruby
class TextProcessor
def initialize(text)
@text = text
end
def process(&block)
if block_given?
@text = block.call(@text)
end
self
end
def upcase
process { |text| text.upcase }
end
def reverse
process { |text| text.reverse }
end
def strip
process { |text| text.strip }
end
def replace(pattern, replacement)
process { |text| text.gsub(pattern, replacement) }
end
def result
@text
end
end
# 使用文本处理器
result = TextProcessor.new(" hello world ")
.strip
.upcase
.replace(/O/, "0")
.reverse
.result
puts result # DL0W 0LLEH🛡️ 方法安全和验证
参数验证
ruby
class SafeMethods
def self.divide(dividend, divisor)
# 参数类型验证
raise ArgumentError, "被除数必须是数字" unless dividend.is_a?(Numeric)
raise ArgumentError, "除数必须是数字" unless divisor.is_a?(Numeric)
# 业务逻辑验证
raise ArgumentError, "除数不能为零" if divisor == 0
dividend / divisor
end
def self.create_user(name, age, email = nil)
# 验证必需参数
raise ArgumentError, "姓名不能为空" if name.nil? || name.empty?
raise ArgumentError, "年龄必须是正数" unless age.is_a?(Integer) && age > 0
# 验证可选参数
if email && !email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
raise ArgumentError, "邮箱格式不正确"
end
{name: name, age: age, email: email}
end
end
# 使用安全方法
begin
result = SafeMethods.divide(10, 2)
puts result # 5
user = SafeMethods.create_user("张三", 25, "zhangsan@example.com")
puts user
rescue ArgumentError => e
puts "错误: #{e.message}"
end方法缓存
ruby
class CachedMethods
def initialize
@cache = {}
end
def expensive_calculation(n)
# 模拟昂贵的计算
cache_key = "expensive_#{n}"
if @cache.key?(cache_key)
puts "从缓存获取结果"
return @cache[cache_key]
end
puts "执行计算"
result = (1..n).reduce(1) { |acc, i| acc * i } # 计算阶乘
@cache[cache_key] = result
result
end
def clear_cache
@cache.clear
end
end
# 使用缓存方法
calculator = CachedMethods.new
puts calculator.expensive_calculation(5) # 执行计算, 120
puts calculator.expensive_calculation(5) # 从缓存获取结果, 120🎯 方法最佳实践
1. 方法设计原则
ruby
# 好的做法:单一职责原则
class UserManager
def create_user(name, email)
validate_user_data(name, email)
user = build_user(name, email)
save_user(user)
end
private
def validate_user_data(name, email)
raise ArgumentError, "姓名不能为空" if name.nil? || name.empty?
raise ArgumentError, "邮箱格式不正确" unless valid_email?(email)
end
def build_user(name, email)
{name: name, email: email, created_at: Time.now}
end
def save_user(user)
# 保存用户逻辑
puts "用户已保存: #{user[:name]}"
end
def valid_email?(email)
email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
end
end2. 参数处理最佳实践
ruby
# 使用关键字参数提高可读性
def create_http_request(
method:,
url:,
headers: {},
body: nil,
timeout: 30,
retries: 3
)
{
method: method.upcase,
url: url,
headers: headers,
body: body,
timeout: timeout,
retries: retries
}
end
# 调用时清晰明了
request = create_http_request(
method: "post",
url: "https://api.example.com/users",
headers: {"Content-Type" => "application/json"},
body: '{"name": "张三"}',
timeout: 60
)
# 使用选项哈希模式
def configure_database(options = {})
config = {
host: options[:host] || "localhost",
port: options[:port] || 5432,
database: options[:database] || "myapp",
username: options[:username] || "user",
password: options[:password] || nil
}
# 配置逻辑
config
end3. 错误处理
ruby
class ApiClient
def get_user(user_id)
# 返回结果或错误
response = make_request("/users/#{user_id}")
case response.status
when 200
parse_user_data(response.body)
when 404
raise UserNotFoundError, "用户不存在: #{user_id}"
when 403
raise PermissionError, "权限不足"
when 500
raise ServerError, "服务器错误"
else
raise ApiError, "未知错误: #{response.status}"
end
end
private
def make_request(endpoint)
# 模拟HTTP请求
OpenStruct.new(status: 200, body: '{"id": 1, "name": "张三"}')
end
def parse_user_data(json_data)
JSON.parse(json_data)
rescue JSON::ParserError => e
raise DataParsingError, "数据解析失败: #{e.message}"
end
end
# 自定义异常类
class ApiError < StandardError; end
class UserNotFoundError < ApiError; end
class PermissionError < ApiError; end
class ServerError < ApiError; end
class DataParsingError < ApiError; end4. 文档化方法
ruby
class MathUtils
# 计算两个数的最大公约数
#
# 使用欧几里得算法计算最大公约数
#
# @param a [Integer] 第一个数
# @param b [Integer] 第二个数
#
# @return [Integer] 最大公约数
#
# @example
# MathUtils.gcd(48, 18) # => 6
# MathUtils.gcd(17, 13) # => 1
#
# @raise [ArgumentError] 当参数不是正整数时抛出
def self.gcd(a, b)
# 参数验证
raise ArgumentError, "参数必须是正整数" unless a.is_a?(Integer) && b.is_a?(Integer)
raise ArgumentError, "参数必须大于0" unless a > 0 && b > 0
# 欧几里得算法
while b != 0
a, b = b, a % b
end
a
end
# 计算数组的平均值
#
# @param numbers [Array<Numeric>] 数字数组
#
# @return [Float] 平均值
#
# @example
# MathUtils.average([1, 2, 3, 4, 5]) # => 3.0
# MathUtils.average([10, 20, 30]) # => 20.0
#
# @raise [ArgumentError] 当数组为空或包含非数字时抛出
def self.average(numbers)
# 参数验证
raise ArgumentError, "参数必须是数组" unless numbers.is_a?(Array)
raise ArgumentError, "数组不能为空" if numbers.empty?
raise ArgumentError, "数组必须包含数字" unless numbers.all? { |n| n.is_a?(Numeric) }
numbers.sum.to_f / numbers.length
end
end📚 下一步学习
掌握了Ruby方法后,建议继续学习:
- Ruby 块和迭代器 - 深入学习代码块的使用
- Ruby Proc和Lambda - 掌握函数式编程特性
- Ruby 元编程 - 学习动态方法定义和调用
- Ruby 设计模式 - 了解常见的设计模式应用
继续您的Ruby学习之旅吧!