Anda dapat menggunakan klon untuk melakukan pemrograman berbasis prototipe di Ruby. Kelas Obyek Ruby mendefinisikan metode klon dan metode dup. Baik klon dan dup menghasilkan salinan dangkal objek yang disalin; artinya, variabel instan objek disalin tetapi bukan objek yang dirujuk. Saya akan menunjukkan contoh:
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"
Perhatikan pada contoh di atas, klon oranye menyalin keadaan (yaitu, variabel instance) dari objek apel, tetapi di mana objek apel referensi objek lain (seperti warna objek String), referensi tersebut tidak disalin. Sebaliknya, apel dan oranye sama-sama merujuk objek yang sama! Dalam contoh kita, referensi adalah objek string 'merah'. Ketika oranye menggunakan metode append, <<, untuk memodifikasi objek String yang ada, itu mengubah objek string menjadi 'oranye merah'. Efek ini juga mengubah apple.color, karena keduanya menunjuk ke objek String yang sama.
Sebagai catatan tambahan, operator penugasan, =, akan menetapkan objek baru dan karenanya menghancurkan referensi. Ini sebuah demonstrasi:
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'
Dalam contoh di atas, ketika kami menetapkan objek baru ke metode instance warna klon oranye, itu tidak lagi merujuk objek yang sama seperti apel. Oleh karena itu, kita sekarang dapat memodifikasi metode warna oranye tanpa mempengaruhi metode warna apel, tetapi jika kita mengkloning objek lain dari apel, objek baru itu akan mereferensikan objek yang sama dalam variabel instance yang disalin seperti apel.
dup juga akan menghasilkan salinan dangkal dari objek yang disalin, dan jika Anda melakukan demonstrasi yang sama seperti ditunjukkan di atas untuk dup, Anda akan melihatnya bekerja dengan cara yang persis sama. Tetapi ada dua perbedaan utama antara clone dan dup. Pertama, seperti yang disebutkan orang lain, klon menyalin keadaan beku dan dup tidak. Apa artinya ini? Istilah 'beku' di Ruby adalah istilah esoterik untuk kekal, yang dengan sendirinya merupakan nomenklatur dalam ilmu komputer, yang berarti bahwa sesuatu tidak dapat diubah. Dengan demikian, objek beku di Ruby tidak dapat dimodifikasi dengan cara apa pun; itu, pada dasarnya, tidak berubah. Jika Anda mencoba mengubah objek yang dibekukan, Ruby akan memunculkan pengecualian RuntimeError. Karena klon menyalin keadaan beku, jika Anda mencoba untuk memodifikasi objek yang dikloning, itu akan meningkatkan pengecualian RuntimeError. Sebaliknya, karena dup tidak menyalin keadaan beku,
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
Kedua, dan, yang lebih menarik, mengkloning kelas singleton (dan karenanya metodenya)! Ini sangat berguna jika Anda ingin melakukan pemrograman berbasis prototipe di Ruby. Pertama, mari kita tunjukkan bahwa memang metode tunggal disalin dengan klon, dan kemudian kita bisa menerapkannya dalam contoh pemrograman berbasis prototipe di 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
Seperti yang Anda lihat, kelas tunggal instance objek buah disalin ke klon. Dan karenanya objek yang dikloning memiliki akses ke metode singleton: seeded ?. Tapi ini tidak terjadi dengan dup:
apple = fruit.dup
=> #<Fruit:0x007fdafe0c6558 @origin=:plant>
apple.seeded?
=> NoMethodError: undefined method `seeded?'
Sekarang dalam pemrograman berbasis prototipe, Anda tidak memiliki kelas yang memperluas kelas lain dan kemudian membuat contoh kelas yang metodenya berasal dari kelas induk yang berfungsi sebagai cetak biru. Alih-alih, Anda memiliki objek dasar dan kemudian Anda membuat objek baru dari objek tersebut dengan metode dan statusnya disalin (tentu saja, karena kami sedang melakukan salinan dangkal melalui klon, objek apa pun yang referensi variabel instan akan dibagi seperti pada JavaScript prototipe). Anda kemudian dapat mengisi atau mengubah status objek dengan mengisi rincian metode yang dikloning. Dalam contoh di bawah ini, kami memiliki objek buah dasar. Semua buah memiliki biji, jadi kami membuat metode number_of_seeds. Tapi apel punya satu biji, jadi kami membuat klon dan mengisi detailnya. Sekarang ketika kita mengkloning apel, kita tidak hanya mengkloning metode tetapi kita mengkloning negara! Ingat klon melakukan salinan negara yang dangkal (variabel instan). Dan karena itu, ketika kita mengkloning apel untuk mendapatkan red_apple, red_apple akan secara otomatis memiliki 1 seed! Anda dapat menganggap red_apple sebagai objek yang mewarisi dari Apple, yang pada gilirannya mewarisi dari Buah. Karenanya, itulah sebabnya saya menggunakan huruf besar untuk Fruit and Apple. Kami menyingkirkan perbedaan antara kelas dan objek milik klon.
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
Tentu saja, kita dapat memiliki metode konstruktor dalam pemrograman berbasis protoype:
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
Pada akhirnya, menggunakan klon, Anda bisa mendapatkan sesuatu yang mirip dengan perilaku prototipe JavaScript.
dup
dan apa yangclone
dilakukan, tetapi mengapa Anda akan menggunakan yang satu dan bukan yang lain.