Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyがオブジェクトを作成する方法を変える

Rubyを素晴らしいものにしていることの1つは、ニーズに合わせてほとんど何でもカスタマイズできることです。これは便利で危険です。足で自分を撃つのは簡単ですが、注意深く使用すると、非常に強力な解決策になる可能性があります。

Ruby Magicでは、便利で危険な組み合わせが優れていると考えています。 Rubyがオブジェクトを作成および初期化する方法と、デフォルトの動作を変更する方法を見てみましょう。

クラスから新しいオブジェクトを作成するための基本

はじめに、Rubyでオブジェクトを作成する方法を見てみましょう。新しいオブジェクトを作成するには (またはインスタンス )、newと呼びます クラスで。他の言語とは異なり、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 オブジェクトはBird.newを使用して作成されます 、およびLogging モジュールは、シングルトンクラスを使用して結果のオブジェクトに含まれます。

シングルトンクラスとは何ですか?

Rubyでは、単一のオブジェクトに固有のメソッドを使用できます。これをサポートするために、Rubyはオブジェクトとその実際のクラスの間に匿名クラスを追加します。メソッドが呼び出されると、シングルトンクラスで定義されたメソッドが、実際のクラスのメソッドよりも優先されます。これらのシングルトンクラスはすべてのオブジェクトに固有であるため、メソッドを追加しても実際のクラスの他のオブジェクトには影響しません。クラスとオブジェクトの詳細については、プログラミングRubyガイドをご覧ください。

各オブジェクトのシングルトンクラスを作成するたびに変更するのは少し面倒です。それでは、Loggingのインクルードを移動しましょう クラスを初期化子に追加して、作成されたすべてのオブジェクトに追加します。

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の包含が失われます クラス:

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 = super
    instance.singleton_class.include(Logging)
    instance
  end
end
 
object = Duck.new('Felix')
object.make_noise
# Started making noise
# Felix: Quack, quack!
# Finished making noise

しかし、make_noiseを呼び出すとどうなりますか イニシャライザで?残念ながら、シングルトンクラスにはLoggingが含まれていないためです。 モジュールはまだ、目的の出力が得られません。

幸いなことに、解決策があります。デフォルトの.newを作成することが可能です。 allocateを呼び出して最初から動作する 。

class Bird
  def self.new(*arguments, &block)
    instance = allocate
    instance.singleton_class.include(Logging)
    instance.send(:initialize, *arguments, &block)
    instance
  end
end

allocateを呼び出す クラスの初期化されていない新しいオブジェクトを返します。したがって、後で追加の動作を含めることができ、その場合にのみ、initializeを呼び出します。 そのオブジェクトのメソッド。 (initializeのため デフォルトではプライベートであるため、sendを使用する必要があります このために)。

Class#allocateについての真実

他の方法とは異なり、allocateをオーバーライドすることはできません。 。 Rubyはallocateにメソッドをディスパッチする従来の方法を使用しません 初めの。その結果、allocateをオーバーライドするだけです newもオーバーライドせずに 動作しません。ただし、allocateを呼び出している場合 直接、Rubyは再定義されたメソッドを呼び出します。 Class#newの詳細 およびClass#allocate Rubyのドキュメントにあります。

なぜこれを行うのですか?

多くの場合と同様に、Rubyがクラスからオブジェクトを作成する方法を変更することは危険であり、予期しない方法で問題が発生する可能性があります。

それでも、オブジェクトの作成を変更するための有効なユースケースがあります。たとえば、ActiveRecordはallocateを使用します 別のinit_from_db 保存されていないオブジェクトを作成するのではなく、データベースからオブジェクトを作成するときに初期化プロセスを変更するメソッド。 allocateも使用します becomesになると、異なる単一テーブル継承タイプ間でレコードを変換します 。

最も重要なことは、オブジェクトの作成をいじることで、Rubyでどのように機能するかについてより深い洞察を得て、さまざまなソリューションに心を開くことができます。記事を楽しんでいただけたでしょうか。

Rubyのデフォルトのオブジェクト作成方法を変更して実装したものについてお聞かせください。 @AppSignalにあなたの考えをツイートすることを躊躇しないでください。


  1. Ruby内部:Rubyオブジェクトのメモリレイアウトの調査

    Ruby内部のクイックツアーをご希望ですか? その後、あなたは御馳走になります。 なぜなら … Rubyオブジェクトがメモリ内にどのように配置されるか、そして内部データ構造を操作していくつかのクールなことを行う方法を一緒に探求します。 シートベルトを締めて、Rubyインタープリターの奥深くへの旅の準備をしてください! アレイのメモリレイアウト 配列を作成するとき、Rubyはそれをシステムメモリと少しのメタデータでバックアップする必要があります。 メタデータに含まれるもの : 配列サイズ(アイテム数) アレイ容量 クラス オブジェクトのステータス(凍結されているかどうか) データが

  2. Ruby Freezeメソッド–オブジェクトの可変性を理解する

    オブジェクトが可変であるとはどういう意味ですか? 派手な言葉で混乱させないでください。「可変性 」は、オブジェクトの内部状態を変更できることを意味します。これは、凍結されたオブジェクトを除く、すべてのオブジェクトのデフォルトです。 、または特別なオブジェクトのリストの一部であるもの。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではありません! 例 : 数字や記号、さらにはtrueには意味がありません またはfalse (オブジェクトでもあります)変更します。 数字の1は常に1になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー