Sie können Clone verwenden, um prototypbasierte Programmierung in Ruby durchzuführen. Die Ruby-Objektklasse definiert sowohl die Klonmethode als auch die Dup-Methode. Sowohl Klon als auch Dup erzeugen eine flache Kopie des Objekts, das kopiert wird. Das heißt, die Instanzvariablen des Objekts werden kopiert, nicht jedoch die Objekte, auf die sie verweisen. Ich werde ein Beispiel zeigen:
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"
Beachten Sie im obigen Beispiel, dass der orangefarbene Klon den Status (dh die Instanzvariablen) des Apple-Objekts kopiert. Wenn das Apple-Objekt jedoch auf andere Objekte (z. B. die String-Objektfarbe) verweist, werden diese Verweise nicht kopiert. Stattdessen verweisen Apfel und Orange auf dasselbe Objekt! In unserem Beispiel ist die Referenz das Zeichenfolgenobjekt 'rot'. Wenn Orange die Append-Methode << verwendet, um das vorhandene String-Objekt zu ändern, ändert es das String-Objekt in 'red orange'. Dies ändert in der Tat auch apple.color, da beide auf dasselbe String-Objekt zeigen.
Als Randnotiz weist der Zuweisungsoperator = ein neues Objekt zu und zerstört so eine Referenz. Hier ist eine Demonstration:
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'
Wenn wir im obigen Beispiel der Farbinstanzmethode des orangefarbenen Klons ein neues Objekt zugewiesen haben, verweist es nicht mehr auf dasselbe Objekt wie Apple. Daher können wir jetzt die Farbmethode von Orange ändern, ohne die Farbmethode von Apple zu beeinflussen. Wenn wir jedoch ein anderes Objekt von Apple klonen, verweist dieses neue Objekt auf dieselben Objekte in kopierten Instanzvariablen wie Apple.
dup erstellt auch eine flache Kopie des Objekts, das kopiert wird. Wenn Sie dieselbe Demonstration wie oben für dup ausführen, sehen Sie, dass dies genauso funktioniert. Es gibt jedoch zwei Hauptunterschiede zwischen Klon und Dup. Erstens kopiert der Klon, wie andere bereits erwähnt haben, den eingefrorenen Zustand und dup nicht. Was bedeutet das? Der Begriff "eingefroren" in Ruby ist ein esoterischer Begriff für unveränderlich, der selbst eine Nomenklatur in der Informatik ist, was bedeutet, dass etwas nicht geändert werden kann. Daher kann ein eingefrorenes Objekt in Ruby in keiner Weise geändert werden. es ist praktisch unveränderlich. Wenn Sie versuchen, ein eingefrorenes Objekt zu ändern, löst Ruby eine RuntimeError-Ausnahme aus. Da der Klon den eingefrorenen Status kopiert, wird beim Versuch, ein geklontes Objekt zu ändern, eine RuntimeError-Ausnahme ausgelöst. Umgekehrt, da dup den eingefrorenen Zustand nicht kopiert,
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
Zweitens und interessanterweise kopiert der Klon die Singleton-Klasse (und damit ihre Methoden)! Dies ist sehr nützlich, wenn Sie eine prototypbasierte Programmierung in Ruby durchführen möchten. Lassen Sie uns zunächst zeigen, dass die Singleton-Methoden tatsächlich mit dem Klon kopiert werden, und dann können wir sie in einem Beispiel für die prototypbasierte Programmierung in Ruby anwenden.
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
Wie Sie sehen können, wird die Singleton-Klasse der Fruchtobjektinstanz in den Klon kopiert. Und daher hat das geklonte Objekt Zugriff auf die Singleton-Methode: seeded?. Dies ist jedoch bei dup nicht der Fall:
apple = fruit.dup
=> #<Fruit:0x007fdafe0c6558 @origin=:plant>
apple.seeded?
=> NoMethodError: undefined method `seeded?'
In der prototypbasierten Programmierung gibt es jetzt keine Klassen, die andere Klassen erweitern und dann Instanzen von Klassen erstellen, deren Methoden von einer übergeordneten Klasse abgeleitet sind, die als Blaupause dient. Stattdessen haben Sie ein Basisobjekt und erstellen dann ein neues Objekt aus dem Objekt, dessen Methoden und Status kopiert wurden (da wir flache Kopien per Klon erstellen, werden natürlich alle Objekte, auf die die Instanzvariablen verweisen, wie in JavaScript freigegeben Prototypen). Sie können dann den Status des Objekts ausfüllen oder ändern, indem Sie die Details der geklonten Methoden eingeben. Im folgenden Beispiel haben wir ein Basisobstobjekt. Alle Früchte haben Samen, also erstellen wir eine Methode number_of_seeds. Aber Äpfel haben einen Samen, und so erstellen wir einen Klon und füllen die Details aus. Wenn wir jetzt Apfel klonen, haben wir nicht nur die Methoden geklont, sondern auch den Staat! Denken Sie daran, dass der Klon eine flache Kopie des Status erstellt (Instanzvariablen). Und aus diesem Grund hat red_apple automatisch 1 Samen, wenn wir Apfel klonen, um einen red_apple zu erhalten! Sie können sich red_apple als ein Objekt vorstellen, das von Apple erbt, das wiederum von Fruit erbt. Deshalb habe ich Obst und Apfel groß geschrieben. Wir haben die Unterscheidung zwischen Klassen und Objekten dank des Klons aufgehoben.
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
Natürlich können wir eine Konstruktormethode in der protoype-basierten Programmierung haben:
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
Letztendlich können Sie mit Klon etwas Ähnliches wie das Verhalten des JavaScript-Prototyps erhalten.
dup
und wasclone
, sondern auch, warum Sie das eine anstelle des anderen verwenden würden.