第1章:Ruby(面向对象之美)
一、导读
1.1 本章学习目标
第1章带我们走进 Ruby 的世界——一门将面向对象做到极致的语言。在 Ruby 中,一切皆对象,连数字和 nil 都是对象。Bruce Tate 选择 Ruby 作为七种语言的第一种,正是因为 Ruby 展示了面向对象编程最优雅的一面。
本章的核心学习目标包括:
- 理解 Ruby 的"一切皆对象"哲学
- 掌握 Ruby 的鸭子类型(Duck Typing)
- 学会使用块(Block)和迭代器
- 理解 Ruby 的元编程能力
- 体验 Ruby 如何用极少的代码表达复杂的逻辑
1.2 为什么本章如此重要
Ruby 的设计哲学是"让程序员快乐"。它的语法优雅、表达力强、元编程能力惊人。很多现代语言的特性——如闭包、模式匹配、管道操作符——都能在 Ruby 中找到影子。理解 Ruby 的设计思想,能让你在所有面向对象语言中写出更优雅的代码。
1.3 核心问题引导
"一切皆对象"在 Ruby 中意味着什么?数字 1 能调用方法吗?
鸭子类型与静态类型有什么本质区别?
Ruby 的块(Block)和 Java 的匿名内部类有什么异同?
Ruby 的元编程如何做到"在运行时修改语言本身"?
method_missing 是如何实现动态方法分派的?
二、核心概念详解
2.1 一切皆对象
# 数字是对象
puts 1.class # Integer
puts 1.odd? # true
puts 1 + 2 # 3(实际上是 1.+(2))
# 字符串是对象
puts "hello".upcase # "HELLO"
puts "hello".length # 5
# nil 也是对象
puts nil.class # NilClass
puts nil.nil? # true
# 甚至可以打开内置类
class Integer
def even?
self % 2 == 0
end
end
puts 4.even? # true2.2 鸭子类型
# 不关心对象的类型,只关心它能做什么
class Duck
def quack; "Quack!"; end
def walk; "Walking like a duck"; end
end
class Person
def quack; "I'm imitating a duck!"; end
def walk; "Walking..."; end
end
def make_it_quack(thing)
puts thing.quack # 不关心 thing 是什么类型,只要能 quack
end
make_it_quack(Duck.new) # "Quack!"
make_it_quack(Person.new) # "I'm imitating a duck!"2.3 块与迭代器
# 块是 Ruby 最强大的特性之一
[1, 2, 3, 4, 5].each { |n| puts n }
# map:转换每个元素
doubled = [1, 2, 3].map { |n| n * 2 } # [2, 4, 6]
# select:过滤
evens = (1..10).select { |n| n.even? } # [2, 4, 6, 8, 10]
# reduce:累积
sum = [1, 2, 3, 4, 5].reduce(0) { |acc, n| acc + n } # 15
# 自定义迭代器
class Fibonacci
include Enumerable
def each
a, b = 0, 1
loop do
yield a
a, b = b, a + b
end
end
end
Fibonacci.new.take(10) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]2.4 元编程
# method_missing:捕获未定义的方法调用
class DynamicLogger
def method_missing(method_name, *args)
level = method_name.to_s.upcase
message = args.first
puts "[#{level}] #{message}"
end
def respond_to_missing?(method_name, include_private = false)
true
end
end
logger = DynamicLogger.new
logger.info("Application started") # [INFO] Application started
logger.error("Something went wrong") # [ERROR] Something went wrong
logger.debug("Variable x = 42") # [DEBUG] Variable x = 42
# define_method:动态定义方法
class Config
def self.setting(name)
define_method(name) do
instance_variable_get("@#{name}")
end
define_method("#{name}=") do |value|
instance_variable_set("@#{name}", value)
end
end
setting :host
setting :port
setting :debug
end
config = Config.new
config.host = "localhost"
config.port = 8080
puts config.host # "localhost"2.5 模块与 Mixin
module Comparable
# 只要定义了 <=>,就自动获得 <, >, <=, >=, between? 等方法
end
module Enumerable
# 只要定义了 each,就自动获得 map, select, reduce 等方法
end
# Mixin:像 Java 的接口 + 默认方法,但更强大
module Loggable
def log(message)
puts "[#{self.class.name}] #{message}"
end
end
class Order
include Loggable
def place
log("Order placed")
end
end
Order.new.place # [Order] Order placed三、实战案例
3.1 案例一:用 Ruby 实现 DSL
class Router
def initialize
@routes = {}
end
def get(path, &block)
@routes["GET #{path}"] = block
end
def post(path, &block)
@routes["POST #{path}"] = block
end
def handle(method, path)
handler = @routes["#{method} #{path}"]
handler ? handler.call : "404 Not Found"
end
end
router = Router.new
router.get("/users") { "User list" }
router.post("/users") { "User created" }
router.get("/users/1") { "User 1" }
puts router.handle("GET", "/users") # User list
puts router.handle("POST", "/users") # User created
puts router.handle("GET", "/unknown") # 404 Not Found3.2 案例二:用 Ruby 的 Enumerable 处理数据
require 'csv'
# 模拟 CSV 数据
data = [
{ name: "Alice", department: "Engineering", salary: 95000 },
{ name: "Bob", department: "Marketing", salary: 75000 },
{ name: "Charlie", department: "Engineering", salary: 105000 },
{ name: "Diana", department: "Marketing", salary: 80000 },
{ name: "Eve", department: "Engineering", salary: 90000 },
]
# 按部门分组并计算平均薪资
result = data
.group_by { |e| e[:department] }
.transform_values { |employees|
employees.map { |e| e[:salary] }.instance_eval { reduce(:+) / size.to_f }
}
puts result
# {"Engineering"=>96666.67, "Marketing"=>77500.0}
# 找出薪资最高的前3名
top3 = data.sort_by { |e| -e[:salary] }.take(3)
top3.each { |e| puts "#{e[:name]}: #{e[:salary]}" }四、本章小结
一切皆对象:Ruby 中所有值都是对象,包括数字、nil、true/false。
鸭子类型:不关心类型,只关心对象能做什么。
块与迭代器:块是 Ruby 函数式编程的核心。
元编程:method_missing、define_method 等让 Ruby 能在运行时修改自身。
模块与 Mixin:比 Java 接口更强大的代码复用机制。
五、思考与练习
Ruby 的鸭子类型相比 Java 的接口有什么优缺点?
Ruby 的块(Block)和 JavaScript 的回调函数有什么异同?
用 Ruby 的元编程实现一个简单的 ActiveRecord 风格的 ORM。