cloneを使用して、Rubyでプロトタイプベースのプログラミングを行うことができます。RubyのObjectクラスは、cloneメソッドとdupメソッドの両方を定義します。cloneとdupはどちらも、コピーするオブジェクトの浅いコピーを作成します。つまり、オブジェクトのインスタンス変数はコピーされますが、参照するオブジェクトはコピーされません。例を示します。
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color << ' orange'
=> "red orange"
apple.color
=> "red orange"
上記の例では、オレンジ色のクローンがappleオブジェクトの状態(つまり、インスタンス変数)をコピーしていますが、appleオブジェクトが他のオブジェクト(Stringオブジェクトの色など)を参照している場合、それらの参照はコピーされません。代わりに、リンゴとオレンジはどちらも同じオブジェクトを参照します。この例では、参照は文字列オブジェクト「red」です。orangeが追加メソッド<<を使用して既存のStringオブジェクトを変更すると、文字列オブジェクトが「red orange」に変更されます。どちらも同じStringオブジェクトを指しているため、これは実際にはapple.colorも変更します。
補足として、代入演算子=は新しいオブジェクトを代入するため、参照を破棄します。ここにデモがあります:
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color = 'orange'
orange.color
=> 'orange'
apple.color
=> 'red'
上記の例では、オレンジのクローンのカラーインスタンスメソッドに新しい新しいオブジェクトを割り当てたときに、アップルと同じオブジェクトを参照しなくなりました。したがって、今度はappleのcolorメソッドに影響を与えずにorangeのcolorメソッドを変更できますが、appleから別のオブジェクトを複製すると、その新しいオブジェクトはコピーされたインスタンス変数内の同じオブジェクトをappleとして参照します。
dupは、コピーするオブジェクトの浅いコピーも作成します。dupに対して上記と同じデモを行うと、まったく同じように機能することがわかります。しかし、cloneとdupの間には2つの大きな違いがあります。最初に、他の人が述べたように、クローンはフリーズ状態をコピーし、dupはコピーしません。これは何を意味するのでしょうか?Rubyの「凍結」という用語は不変の難解な用語であり、それ自体がコンピュータサイエンスの命名法であり、変更できないものを意味します。したがって、Rubyでフリーズされたオブジェクトを変更することはできません。実際には不変です。フリーズしたオブジェクトを変更しようとすると、RubyはRuntimeError例外を発生させます。cloneは凍結状態をコピーするため、複製されたオブジェクトを変更しようとすると、RuntimeError例外が発生します。逆に、dupは凍結状態をコピーしないため、
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.frozen?
=> false
apple.freeze
apple.frozen?
=> true
apple.color = 'crimson'
RuntimeError: can't modify frozen Apple
apple.color << ' crimson'
=> "red crimson" # we cannot modify the state of the object, but we can certainly modify objects it is referencing!
orange = apple.dup
orange.frozen?
=> false
orange2 = apple.clone
orange2.frozen?
=> true
orange.color = 'orange'
=> "orange" # we can modify the orange object since we used dup, which did not copy the frozen state
orange2.color = 'orange'
RuntimeError: can't modify frozen Apple # orange2 raises an exception since the frozen state was copied via clone
次に、さらに興味深いことに、クローンはシングルトンクラス(およびそのメソッド)をコピーします。これは、Rubyでプロトタイプベースのプログラミングを行う場合に非常に役立ちます。最初に、シングルトンメソッドが実際に複製でコピーされ、次にRubyでのプロトタイプベースのプログラミングの例にそれを適用できることを示しましょう。
class Fruit
attr_accessor :origin
def initialize
@origin = :plant
end
end
fruit = Fruit.new
=> #<Fruit:0x007fc9e2a49260 @origin=:plant>
def fruit.seeded?
true
end
2.4.1 :013 > fruit.singleton_methods
=> [:seeded?]
apple = fruit.clone
=> #<Fruit:0x007fc9e2a19a10 @origin=:plant>
apple.seeded?
=> true
ご覧のように、フルーツオブジェクトインスタンスのシングルトンクラスがクローンにコピーされます。したがって、クローンオブジェクトはシングルトンメソッド:seeded?にアクセスできます。しかし、これはdupには当てはまりません。
apple = fruit.dup
=> #<Fruit:0x007fdafe0c6558 @origin=:plant>
apple.seeded?
=> NoMethodError: undefined method `seeded?'
現在、プロトタイプベースのプログラミングでは、他のクラスを拡張し、ブループリントとして機能する親クラスからメソッドが派生するクラスのインスタンスを作成するクラスはありません。代わりに、ベースオブジェクトがあり、そのメソッドと状態がコピーされたオブジェクトから新しいオブジェクトを作成します(もちろん、クローンを介して浅いコピーを行っているため、インスタンス変数が参照するオブジェクトはJavaScriptと同じように共有されますプロトタイプ)。次に、複製されたメソッドの詳細を入力することにより、オブジェクトの状態を入力または変更できます。以下の例では、ベースフルーツオブジェクトがあります。すべての果物には種子があるので、number_of_seedsメソッドを作成します。ただし、リンゴにはシードが1つあるため、クローンを作成して詳細を入力します。これで、appleのクローンを作成するときに、メソッドのクローンを作成するだけでなく、状態のクローンも作成しました!cloneは状態(インスタンス変数)の浅いコピーを行うことを覚えておいてください。そのため、red_appleを取得するためにappleのクローンを作成すると、red_appleには自動的に1つのシードがあります。red_appleは、Appleから継承するオブジェクトであり、AppleはFruitから継承するオブジェクトと考えることができます。したがって、それが私がフルーツとアップルを活用した理由です。クラスとオブジェクトの違いはクローンのおかげでなくなりました。
Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
@number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
@number_of_seeds
end
Apple = Fruit.clone
=> #<Object:0x007fb1d78165d8>
Apple.number_of_seeds = 1
Apple.number_of_seeds
=> 1
red_apple = Apple.clone
=> #<Object:0x007fb1d892ac20 @number_of_seeds=1>
red_apple.number_of_seeds
=> 1
もちろん、プロトタイプベースのプログラミングでコンストラクタメソッドを使用できます。
Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
@number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
@number_of_seeds
end
def Fruit.init(number_of_seeds)
fruit_clone = clone
fruit_clone.number_of_seeds = number_of_seeds
fruit_clone
end
Apple = Fruit.init(1)
=> #<Object:0x007fcd2a137f78 @number_of_seeds=1>
red_apple = Apple.clone
=> #<Object:0x007fcd2a1271c8 @number_of_seeds=1>
red_apple.number_of_seeds
=> 1
最終的には、クローンを使用して、JavaScriptプロトタイプの動作に似たものを取得できます。
dup
してclone
いますが、なぜあなたは他のではなく、1を使用すると思います。