Rubyのオブジェクト作成方法を改変する(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Changing the Way Ruby Creates Objects | AppSignal Blog 原文公開日: 2018/08/07 著者: Benedikt Deicke(@benediktdeicke) — Userlist.ioのCTOであり書籍『SaaS applications in Ruby on Rails』の著者です。 サイト: AppSignal Rubyのオブジェクト作成方法を改変する(翻訳) Rubyの素晴らしさのひとつに、必要に応じてほぼ何でもカスタマイズできるという点があげられます。カスタマイズは便利であると同時に危険も伴うので、うっかりすると簡単に自分の足を撃ち抜いてしまいますが、十分気をつければ相当強力なソリューションを生み出すこともできます。 「Ruby Magic」では、便利さと危険は名コンビであると考えます。それではRubyのオブジェクト初期化方法を調べ、デフォルトの振る舞いを改変してみましょう。 クラスからの新規オブジェクト作成の基本 最初に、Rubyがオブジェクトを作成する方法を見てみましょう。新しいオブジェクト(インスタンス)を作成するには、そのクラスでnewを呼び出します。他の言語と異なり、Rubyのnewは言語のキーワードではなくメソッドであり、次のように他のメソッドとまったく同じように呼び出されます。 class Dog end object = Dog.new このnewメソッドに引数を渡すことで、新しく作成されたオブジェクトをカスタマイズできます。引数として渡したものは、種類を問わずイニシャライザに渡されます。 class Dog def initialize(name) @name = name end end object = Dog.new(‘Good boy’) 繰り返しますが、Rubyのイニシャライザは他の言語のような特殊な構文やキーワードではなく、単なるメソッドです。 ということは、Rubyの他のメソッドと同様に、こうしたイニシャライザメソッドにもちょっかいを出せるのではないでしょうか?もちろん可能です! 単独のオブジェクトの振る舞いを改変する 特定のクラスから派生するどのオブジェクトからも、常にログを出力したいとしましょう(メソッドがサブクラスでオーバーライドされたときにもです)。これを実現する方法のひとつは、そのオブジェクトのシングルトンクラスにモジュールを1つ追加することです。 module Logging def make_noise puts “Started making noise” super puts “Finished making noise” end end class Bird def make_noise puts “Chirp, chirp!” end end object = Bird.new object.singleton_class.include(Logging) object.make_noise # Started making noise # Chirp, chirp! # Finished making noise 上の例ではBird.newでBirdオブジェクトが1つ作成され、シングルトンクラスを使って、そのオブジェクトにLoggingモジュールがincludeされます。 シングルトンクラスとは Rubyでは特定のオブジェクトだけで使える固有のメソッドを利用できます。Rubyはこの機能をサポートするために、そのオブジェクトと実際のクラスの間に無名クラスを1つ追加します。メソッドが呼び出されると、実際のクラスにあるメソッドよりも、このシングルトンクラスで定義されているメソッドが優先されます。このシングルトンクラスは他のどのオブジェクトとも異なる固有のものなので、そこにメソッドを追加しても、実際のクラスから派生するオブジェクトには何の影響も生じません。詳しくはProgramming Ruby guideをご覧ください。 オブジェクトが作成されるたびにそのシングルトンクラスをいちいち変更するのはちょっとイケてません。そこでLoggingクラスのincludeをイニシャライザに移して、作成されるすべてのオブジェクトに追加されるようにしましょう。 module Logging def make_noise puts “Started making noise” super puts “Finished making noise” end end class Bird def initialize singleton_class.include(Logging) end def make_noise puts “Chirp, chirp!” end end object = Bird.new object.make_noise # Started making noise # Chirp, chirp! # Finished making noise この方法はうまくいきますが、Birdのサブクラス(Duckなど)を作成する場合は、イニシャライザでsuperを呼んでLoggingの振る舞いを維持する必要があります。メソッドをオーバーライドする場合は常にsuperを正しく呼び出すのがよいとする考えもあるのですが、それなしでできる方法がないかどうか探してみましょう。 サブクラスでsuperを呼ばないと、Loggerクラスはincludeされません。 class Duck < Bird def initialize(name) @name = name end def make_noise puts “#{@name}: Quack, quack!” end end object = Duck.new(‘Felix’) object.make_noise # Felix: Quack, quack! では代わりにBird.newをオーバーライドしましょう。前述のとおりnewはクラスに実装されたメソッドのひとつに過ぎないので、これをオーバーライドしてsuperを呼べば、新しく作成されるオブジェクトを望みのままに改変できるのです。 class Bird def self.new(*arguments, &block) instance … Continue reading Rubyのオブジェクト作成方法を改変する(翻訳)