Menu
CoddyTech

模块与 include

本课属于 Coddy Ruby 学习旅程的“面向对象编程”单元。第 27 课,共 57 课。

模块是一组有名称的方法。它不能创建对象,但类可以include它,这样每个实例就都拥有该模块的方法,就像这些方法是由类定义的一样。这称为混入:

module Greetable
  def greet
    "Hello, I am #{name}"
  end
end

class Person
  include Greetable
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

puts Person.new("Ada").greet

输出:

Hello, I am Ada

模块的方法可以调用类提供的方法,例如上面的 name。因此,只要每个类都提供模块所使用的方法,一个模块就可以服务于各种不同的类:

module Greetable
  def greet
    "Hello, I am #{name}"
  end
end

class Robot
  include Greetable

  def name
    "R2"
  end
end

class City
  include Greetable

  def name
    "Paris"
  end
end

puts Robot.new.greet
puts City.new.greet

输出:

Hello, I am R2
Hello, I am Paris

一个类只有一个父类,但可以包含任意数量的模块。include? 用来检查一个类是否包含某个模块,该模块会出现在 ancestors 中,紧跟在类之后。Ruby 会按这个顺序查找方法:

module Swimmer
  def swim
    "swimming"
  end
end

module Flyer
  def fly
    "flying"
  end
end

class Duck
  include Swimmer
  include Flyer
end

d = Duck.new
puts "#{d.swim}, #{d.fly}"
puts Duck.include?(Flyer)
p Duck.ancestors.take(3)

输出:

swimming, flying
true
[Duck, Flyer, Swimmer]

模块还会将相关的类和常量归到同一个名称下,也就是一个命名空间,这样名称就不会冲突。Shop::Item 和 Game::Item 是不同的类:

module Shop
  TAX = 0.2

  class Item
    def label
      "shop item, tax #{TAX}"
    end
  end
end

puts Shop::Item.new.label
puts Shop::TAX

输出:

shop item, tax 0.2
0.2
challenge icon

挑战

简单

完成这些模块和类。Greetable#greet 使用对象的 name,并返回 Hello, I am Ada。Walkable#walk(steps) 会把步数加到存储在 @steps 中的计数器上(第一次从 nil 开始),并返回 Ada walked 30 steps,也就是累计步数。Person 包含这两个模块;Robot 只包含 Greetable,它的名称由 R- 和序列号组成。

提供的代码会根据第一行(Ada 7)创建一个人和一个机器人,然后运行 greet p、greet r 或 walk 10(针对这个人)这些命令,并打印每条命令的结果,最后输出 robot walks: false。

你的代码要写在 greetable.rb、walkable.rb、person.rb 和 robot.rb 中。main.rb 包含提供的输入/输出代码,不能编辑。

亲手试试

require_relative 'greetable'
require_relative 'walkable'
require_relative 'person'
require_relative 'robot'

# 提供的输入输出代码:保持原样
input = []
while (line = gets)
  input << line.chomp
end
name, serial = input[0].split
person = Person.new(name)
robot = Robot.new(serial)
input.drop(1).each do |line|
  command, arg = line.split
  if command == "walk"
    puts person.walk(arg.to_i)
  else
    target = arg == "p" ? person : robot
    puts target.greet
  end
end
puts "robot walks: #{Robot.include?(Walkable)}"
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Ruby 在线编译器