Menu
Coddy logo textTech

ポリモーフィズム

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

ポリモーフィズムとは、異なるクラスのオブジェクトに対して1つのメソッド呼び出しが機能し、それぞれのオブジェクトが独自の方法で応答することです。図形をループ処理するコードは、各図形の種類を確認せずに、それぞれに対してareaを呼び出します:

class Shape
  def to_s
    format("%s %.2f", self.class.name.downcase, area)
  end
end

class Circle < Shape
  def initialize(r)
    @r = r
  end

  def area
    3.14159 * @r * @r
  end
end

class Square < Shape
  def initialize(side)
    @side = side
  end

  def area
    @side * @side
  end
end

shapes = [Circle.new(1), Square.new(3)]
shapes.each { |s| puts s }
puts format("total %.2f", shapes.sum(&:area))

出力:

circle 3.14
square 9.00
total 12.14

ポリモーフィズムがなければ、呼び出し側はすべてのオブジェクトの種類を確認し、違いが関係する場所ごとにその確認を繰り返さなければなりません。ポリモーフィズムがあれば、その違いは各クラスの内部に存在します。

# ポリモーフィズムなし
case shape.kind
when "circle" then 3.14159 * shape.r ** 2
when "square" then shape.side ** 2
end

# ポリモーフィズムあり
shape.area

新しい種類のオブジェクトを追加しても、呼び出し側のコードを変更する必要はありません。独自の area を持つ新しいサブクラスは、既存のすべてのループで機能します。

class Shape
  def describe
    "area #{area}"
  end
end

class Square < Shape
  def area
    4
  end
end

class Triangle < Shape
  def area
    6
  end
end

[Square.new, Triangle.new].each { |s| puts s.describe }

出力:

area 4
area 6

to_s は身近な例です。puts は任意のオブジェクトに対してこれを呼び出し、クラスごとに異なる結果を返します。あなたのクラスも独自の to_s を定義することで参加できます。

class Coin
  def to_s
    "a coin"
  end
end

[42, "text", nil, [1, 2], Coin.new].each { |x| puts "[#{x}]" }

出力:

[42]
[text]
[]
[[1, 2]]
[a coin]
challenge icon

チャレンジ

簡単

図形を完成させてください。Shape#to_s は、クラス名を小文字にし、面積を小数点以下2桁で表示した circle: 3.14 を返します。また、各サブクラスは area を提供します:Circle.new(r)(円周率には 3.14159 を使用)、Rectangle.new(w, h)、Triangle.new(base, height)。

用意されているコードは、circle 1、rectangle 2 3、または triangle 4 5 の行を読み取り、すべての図形を出力した後、total: 22.14 と largest: rectangle: 6.00 を出力します。使用するのは to_s と area だけです。

コードは shape.rb、circle.rb、rectangle.rb、triangle.rb に記述します。main.rb には用意された入出力コードが含まれており、編集できません。

自分で試してみよう

require_relative 'shape'
require_relative 'circle'
require_relative 'rectangle'
require_relative 'triangle'

# 提供された入出力コード: そのままにしておく
input = []
while (line = gets)
  input << line.chomp
end
kinds = { "circle" => Circle, "rectangle" => Rectangle, "triangle" => Triangle }
shapes = input.map do |line|
  kind, *numbers = line.split
  kinds[kind].new(*numbers.map(&:to_f))
end
shapes.each { |s| puts s }
puts format("total: %.2f", shapes.sum(&:area))
puts "largest: #{shapes.max_by(&:area)}"
quiz icon腕試し

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

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

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