Menu
Coddy logo textTech

モジュールと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のように、クラスが提供するメソッドを呼び出すことができます。そのため、各クラスがモジュールで使用されるものを提供している限り、1つのモジュールでまったく異なるクラスに対応できます。

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

クラスにはスーパークラスが1つだけありますが、任意の数のモジュールを含めることができます。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]

モジュールは、関連するクラスや定数を1つの名前、つまり名前空間の下にまとめ、名前が衝突しないようにします。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-を付けたものになります。

提供されたコードは、1行目(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オンラインコンパイラ