Menu
Coddy logo textTech

オブジェクトの拡張

CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 28/57。

extendは、モジュールのメソッドをすべてのインスタンスではなく、1つのオブジェクトに追加します。クラスを拡張すると、それらはクラスオブジェクトに追加されるため、クラスメソッドになります:

module Describable
  def describe
    "#{name} has #{instance_methods(false).size} methods"
  end
end

class Dog
  extend Describable

  def bark
    "Woof"
  end

  def sit
    "sits"
  end
end

puts Dog.describe

出力:

Dog has 2 methods

extendとともに使用するモジュールは、クラスのインスタンス変数に状態を保持できます。それを拡張する各クラスは、クラスのインスタンス変数として、それぞれ独自のコピーを持ちます。

module Counted
  def created
    @created ||= 0
  end

  def record
    @created = created + 1
  end
end

class Order
  extend Counted
end

class Invoice
  extend Counted
end

Order.record
Order.record
Invoice.record
puts "#{Order.created} #{Invoice.created}"

出力:

2 1

extendは単一のオブジェクトにも作用します。メソッドを取得するのはそのオブジェクトだけであり、同じクラスの他のインスタンスは変更されません。また、拡張されたオブジェクトに対してのみis_a?はtrueになります。

module Shiny
  def polish
    "#{self} is shining"
  end
end

a = "car"
b = "bike"
a.extend(Shiny)
puts a.polish
puts a.is_a?(Shiny)
puts b.respond_to?(:polish)

出力:

car is shining
true
false

この2つのキーワードは、同じモジュールのメソッドを異なる場所に配置します。includeはクラスのインスタンスメソッドにし、extendは拡張された1つのオブジェクトのメソッドにします。

class Order
  include Printable    # order.print_me       (すべてのインスタンス)
  extend Counted       # Order.record         (クラス自体)
end

item.extend(Shiny)     # item.polish          (このオブジェクトのみ)
challenge icon

チャレンジ

簡単

クラスがextendで使用するRegistryモジュールを完成させてください。register(name)はクラス独自のリストに名前を追加します。このリストは@entriesに保持され、初回使用時に作成されます。そしてPlugin registered xを返します。すでに存在する名前の場合はPlugin already has xを返します。entriesはリストを返し、countはそのサイズを返します。PluginとThemeはどちらもRegistryを拡張し、それぞれ別々のリストを保持します。

提供されたコードはplugin nameまたはtheme nameの形式で行を読み取り、該当するクラスでregisterを呼び出して、その結果を出力します。最後にplugins: a, b (2)とthemes: ...を出力し、リストが空の場合はnone (0)を出力します。

コードはregistry.rb、plugin.rb、theme.rbに記述してください。main.rbには提供された入出力コードが含まれており、編集できません。

自分で試してみよう

require_relative 'registry'
require_relative 'plugin'
require_relative 'theme'

# 提供された入出力コード: そのままにしておく
input = []
while (line = gets)
  input << line.chomp
end
input.each do |line|
  kind, name = line.split
  target = kind == "plugin" ? Plugin : Theme
  puts target.register(name)
end
[["plugins", Plugin], ["themes", Theme]].each do |label, klass|
  list = klass.entries.empty? ? "none" : klass.entries.join(", ")
  puts "#{label}: #{list} (#{klass.count})"
end
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rubyオンラインコンパイラ