tkykmw code

学びたいことを学ぶ

RubyのStructを継承して、共通の振る舞いを追加する

Struct.new の戻り値は Structインスタンスではなく、サブクラスである。

Person = Struct.new(:name, :age)
Person.is_a?(Struct) #=> false
Person < Struct #=> true

しかし「 new によってサブクラスが作られる」という振る舞いそのものは継承されない。Person.new の戻り値は、当然期待されるとおり、 Personインスタンスであって、サブクラスではない。

Person.new("ss", 20).is_a?(Person) #=> true

一方で、通常のクラス継承によって Struct のサブクラスを作れば、Struct.new の振る舞いも継承できる。

class MyStruct < Struct; end

KlassFromMyStruct = MyStruct.new(:name)
KlassFromMyStruct.is_a?(MyStruct) #=> false
KlassFromMyStruct < MyStruct #=> true

MyStruct に定義したインスタンスメソッドは、「MyStruct.new で作られたサブクラス」のインスタンスから呼び出せるので、ここで共通の振る舞いを定義できる。

class MyStruct < Struct
  def i_method
    "instance method"
  end
end

KlassFromMyStruct2 = MyStruct.new(:name)
KlassFromMyStruct2.new('hoge').i_method #=> "instance method"
KlassFromMyStruct2.i_method #=> NoMethodError

クラスメソッドも、期待通りに書ける。

class MyStruct < Struct
  class <<self
    def c_method
      "class method"
    end
  end
end

MyStruct.new(:name).c_method #=> "class method"

やや直感に反するものの、理解すれば当然の動作である。