更新された質問は、今ではまったく異なって見えます。私があなたを正しく理解しているなら、あなたはオブジェクトの割り当てと初期化にフックしたいと思うでしょう。それはメタクラスとはまったく関係がありません。(しかし、あなたはまだあなたが実際にやりたいことを書いていないので、私はまだオフになっているかもしれません。)
一部のオブジェクト指向言語では、オブジェクトはコンストラクターによって作成されます。ただし、Rubyにはコンストラクターがありません。コンストラクターは単なるファクトリメソッドです(愚かな制限があります)。代わりに(より強力な)ファクトリメソッドを使用できるのであれば、適切に設計された言語でそれらを使用する理由はありません。
Rubyでのオブジェクト構築は、次のように機能します。オブジェクト構築は、割り当てと初期化の2つのフェーズに分割されます。割り当てはallocate
、クラスのインスタンスメソッドとして定義され、Class
通常はオーバーライドされない、と呼ばれるパブリッククラスメソッドによって行われます。(実際、実際にオーバーライドできるとは思いません。)オブジェクトにメモリスペースを割り当て、いくつかのポインタを設定するだけですが、現時点ではオブジェクトは実際には使用できません。
initialize
そこでイニシャライザが登場します。これは、オブジェクトの内部状態を設定し、他のオブジェクトで使用できる一貫性のある完全に定義された状態にする、と呼ばれるインスタンスメソッドです。
したがって、新しいオブジェクトを完全に作成するには、次のようにする必要があります。
x = X.allocate
x.initialize
[注:Objective-Cプログラマーはこれを認識している可能性があります。]
ただし、呼び出すのを忘れがちでinitialize
あり、原則として、オブジェクトは構築後に完全に有効である必要があるため、と呼ばれる便利なファクトリメソッドがありClass#new
ます。これは、すべての作業を実行し、次のようになります。
class Class
def new(*args, &block)
obj = allocate
obj.initialize(*args, &block)
return obj
end
end
[注:実際にinitialize
はプライベートであるため、次のようなアクセス制限を回避するには、リフレクションを使用する必要があります:obj.send(:initialize, *args, &block)
]
ちなみに、これが、パブリッククラスメソッドを呼び出すオブジェクトを作成するのに、プライベートインスタンスメソッドFoo.new
を実装Foo#initialize
する理由です。これは、多くの新規参入者をつまずかせるようです。
ただし、これはいずれも言語に組み込まれていません。通常、任意のクラスのプライマリファクトリメソッドが呼び出されるという事実new
は単なる慣例です(Javaのコンストラクターに似ていますが、完全に異なるため、異なる場合があります)。他の言語では、コンストラクターには特定の名前を付ける必要があります。Javaでは、クラスと同じ名前にする必要があります。つまり、a)コンストラクターは1つしか存在できず、b)匿名クラスは名前がないため、コンストラクターを持つことができません。Pythonでは、ファクトリメソッドを呼び出す必要があります__new__
、これも1つしか存在できないことを意味します。(JavaとPythonの両方で、もちろん異なるファクトリメソッドを使用できますが、それらを呼び出すことはデフォルトを呼び出すこととは異なって見えますが、Ruby(およびこのパターンが発生した元のSmalltalk)ではまったく同じように見えます。)
Rubyでは、ファクトリメソッドは好きなだけ名前を付けることができ、ファクトリメソッドにはさまざまな名前を付けることができます。(たとえば、コレクションクラスの場合、ファクトリメソッドは多くの場合、にエイリアスされます。これにより、配列のように見える代わりに[]
記述できるため、リストのコレクションっぽい性質が強調されます。)List[1, 2, 3]
List.new(1, 2, 3)
要するに:
- 標準化されたファクトリメソッドはですが
Foo.new
、何でもかまいません
Foo.new
allocate
空のオブジェクトにメモリを割り当てるための呼び出しfoo
Foo.new
foo.initialize
次に、Foo#initialize
インスタンスメソッドを呼び出します
- これらの3つはすべて、他のメソッドと同じように、定義を解除、再定義、オーバーライド、ラップ、エイリアスなどを行うことができるメソッドです。
- ただし
allocate
、Rubyでは実際にはできないRubyランタイム内にメモリを割り当てる必要があります。
Pythonでは、__new__
おおよそとRubyの両方に対応しnew
、allocate
Rubyでは__init__
正確に対応しinitialize
ます。主な違いは、Rubyではnew
呼び出しinitialize
が行われるのに対し、Pythonではランタイム__init__
が。の後に自動的に呼び出すこと__new__
です。
たとえば、これは最大2つのインスタンスの作成のみを許可するクラスです。
class Foo
def self.new(*args, &block)
@instances ||= 0
raise 'Too many instances!' if @instances >= 2
obj = allocate
obj.send(:initialize, *args, &block)
@instances += 1
return obj
end
attr_reader :name
def initialize(name)
@name = name
end
end
one = Foo.new('#1')
two = Foo.new('#2')
puts two.name # => #2
three = Foo.new('#3') # => RuntimeError: Too many instances!