Ruby でプロトタイプベースのOOPは可能か

オブジェクト指向JavaScriptの原則

オブジェクト指向JavaScriptの原則



JavaScript では以下のように書けます。

var person = {
  name : "Matsumoto",
  sayName : function () {
    console.log(this.name);
  }
};

person.sayName();    //=>"Matsumoto"

これは JavaScript では自然ですが、Ruby で同等のことは可能でしょうか。いわゆる「特異メソッド」を使ってみます。

person = Object.new
def person.name
  "Matsumoto"
end
def person.sayName
  puts self.name
end

person.sayName    #=>"Matsumoto"

似たようなことは一応可能ですね。


では次の JavaScript コードはどうでしょう。

function sayNameForAll() {
  console.log(this.name)
}

var person1 = {
  name : "Matsumoto",
  sayName : sayNameForAll
};

var person2 = {
  name : "Larry",
  sayName : sayNameForAll
};

var name = "van Rossum";

person1.sayName();   //=>"Matsumoto"
person2.sayName();   //=>"Larry"
sayNameForAll();     //=>"van Rossum"

これは Ruby なら

def sayNameForAll
  puts self.name
end

person1 = Object.new
def person1.name
  "Matsumoto"
end
def person1.sayName
  sayNameForAll
end

person2 = person1.clone
def person2.name
  "Larry"
end
def person2.sayName
  sayNameForAll
end

def self.name
  "van Rossum"
end

person1.sayName    #=>"Matsumoto"
person2.sayName    #=>"Larry"
sayNameForAll      #=>"van Rossum"

で、これもだいたい JavaScript と似たようなことが出来ていますね。
しかしこれなら、もうクラスベースで、

class Person
  def initialize(name)
    @name = name
  end

  def sayName
    puts @name
  end
end

person1 = Person.new("Matsumoto")
person2 = Person.new("Larry")

person1.sayName    #=>"Matsumoto"
person2.sayName    #=>"Larry"

とやりたくなるのですけれど。プロトタイプベースのいいところって何だろう。ああ、そうか、特定のインスタンス

def person1.virtue
  "gentleman"
end

puts person1.virtue    #=>"gentleman"

こういうことができることか。Ruby では Python みたいにいきなり person1.virtue = "gentleman" という書き方はできないのだけれど、これは意図的なんだろうな。もちろん、さらに

class Person
  attr_accessor :virtue
end

person1.virtue = "gentleman"
person2.virtue = "interesting"

puts person1.virtue    #=>"gentleman"
puts person2.virtue    #=>"interesting"

ということはできる。またさらに

class Person
  def putout
    puts @virtue
  end
end

person1.putout    #=>"gentleman"

なので、インスタンス変数を明示的に使う前にアクセサを宣言してもよいのだな。