1Seven Languages in Seven Weeks

Ruby

面向对象之美

鸭子类型块与迭代器元编程DSL

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第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 一切皆对象

ruby
# 数字是对象
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?    # true

2.2 鸭子类型

ruby
# 不关心对象的类型,只关心它能做什么
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
# 块是 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 元编程

ruby
# 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

ruby
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

ruby
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 Found

3.2 案例二:用 Ruby 的 Enumerable 处理数据

ruby
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。


1/7Ruby
0%