คุณสามารถใช้โคลนเพื่อทำการเขียนโปรแกรมต้นแบบใน Ruby คลาส Object ของรูบี้กำหนดทั้งวิธีการโคลนและวิธีการซ้ำ ทั้งโคลนและสำเนาสร้างสำเนาวัตถุที่คัดลอกมาตื้น ๆ ; นั่นคือตัวแปรอินสแตนซ์ของวัตถุจะถูกคัดลอก แต่ไม่ใช่วัตถุที่พวกเขาอ้างอิง ฉันจะแสดงตัวอย่าง:
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) การอ้างอิงเหล่านั้นจะไม่ถูกคัดลอก ในทางกลับกันแอปเปิ้ลและสีส้มต่างอ้างถึงวัตถุเดียวกัน! ในตัวอย่างของเราการอ้างอิงคือวัตถุสตริง 'สีแดง' เมื่อสีส้มใช้วิธีการผนวก, <<, เพื่อปรับเปลี่ยนวัตถุสตริงที่มีอยู่มันเปลี่ยนวัตถุสตริงเป็น 'สีส้มสีแดง' สิ่งนี้มีผลในการเปลี่ยนแปลง apple.color เช่นกันเนื่องจากทั้งคู่ชี้ไปที่วัตถุ String เดียวกัน
ในฐานะที่เป็นบันทึกด้านข้างผู้ประกอบการที่ได้รับมอบหมาย = จะกำหนดวัตถุใหม่และทำลายการอ้างอิง นี่คือการสาธิต:
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'
ในตัวอย่างข้างต้นเมื่อเรากำหนดวัตถุใหม่ที่สดใหม่ให้กับวิธีการอินสแตนซ์สีของโคลนสีส้มมันไม่ได้อ้างอิงวัตถุเดียวกันกับแอปเปิ้ลอีกต่อไป ดังนั้นตอนนี้เราสามารถปรับเปลี่ยนวิธีสีของส้มโดยไม่ส่งผลกระทบต่อวิธีการสีของแอปเปิ้ล แต่ถ้าเราโคลนวัตถุอื่นจากแอปเปิ้ลวัตถุใหม่นั้นจะอ้างอิงวัตถุเดียวกันในตัวแปรอินสแตนซ์ที่คัดลอกเป็นแอปเปิ้ล
ดูปองท์จะสร้างสำเนาตื้น ๆ ของวัตถุที่กำลังคัดลอกและหากคุณทำการสาธิตแบบเดียวกันกับที่แสดงข้างบนเป็นคู่คุณจะเห็นว่ามันทำงานในลักษณะเดียวกัน แต่มีความแตกต่างที่สำคัญระหว่างโคลนและสอง ครั้งแรกตามที่คนอื่นพูดถึงการคัดลอกโคลนสถานะแช่แข็งและสองไม่ได้ สิ่งนี้หมายความว่า? คำว่า 'แช่แข็ง' ในรูบีเป็นคำลึกลับสำหรับการเปลี่ยนรูปแบบซึ่งตัวเองเป็นศัพท์ในวิทยาศาสตร์คอมพิวเตอร์ซึ่งหมายความว่าสิ่งที่ไม่สามารถเปลี่ยนแปลงได้ ดังนั้นวัตถุแช่แข็งใน Ruby ไม่สามารถแก้ไขได้ในทางใดทางหนึ่ง มันคือผลไม่เปลี่ยนรูป หากคุณพยายามที่จะแก้ไขวัตถุแช่แข็ง Ruby จะเพิ่มข้อยกเว้น RuntimeError เนื่องจากโคลนคัดลอกสถานะน้ำแข็งถ้าคุณพยายามปรับเปลี่ยนวัตถุโคลนมันจะยกข้อยกเว้น RuntimeError ตรงกันข้ามเนื่องจากสำเนาไม่ได้คัดลอกสถานะแช่แข็ง
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? แต่นี่ไม่ใช่กรณีที่มีซ้ำ:
apple = fruit.dup
=> #<Fruit:0x007fdafe0c6558 @origin=:plant>
apple.seeded?
=> NoMethodError: undefined method `seeded?'
ในขณะนี้ในการเขียนโปรแกรมโดยใช้ต้นแบบคุณไม่มีคลาสที่ขยายคลาสอื่นแล้วสร้างอินสแตนซ์ของคลาสที่วิธีการสืบทอดมาจากคลาสพาเรนต์ที่ทำหน้าที่เป็นพิมพ์เขียว แต่คุณมีวัตถุฐานแล้วสร้างวัตถุใหม่จากวัตถุด้วยวิธีการและสถานะที่คัดลอก (แน่นอนเนื่องจากเราทำสำเนาตื้นผ่านโคลนวัตถุใด ๆ ที่การอ้างอิงตัวแปรอินสแตนซ์จะถูกแชร์ใน JavaScript ต้นแบบ) จากนั้นคุณสามารถเติมหรือเปลี่ยนสถานะของวัตถุโดยการกรอกรายละเอียดของวิธีการโคลน ในตัวอย่างด้านล่างเรามีวัตถุผลไม้ฐาน ผลไม้ทุกชนิดมีเมล็ดดังนั้นเราจึงสร้างเมธอด number_of_seeds แต่แอปเปิ้ลมีหนึ่งเมล็ดดังนั้นเราจึงสร้างโคลนและกรอกรายละเอียด ตอนนี้เมื่อเราโคลนแอปเปิ้ลเราไม่เพียง แต่โคลนวิธี แต่เราโคลนรัฐ! โปรดจำไว้ว่าโคลนทำสำเนาของรัฐที่ตื้น (ตัวแปรอินสแตนซ์) และด้วยเหตุนี้เมื่อเราโคลนแอปเปิ้ลเพื่อรับ red_apple, red_apple จะมี 1 เมล็ดโดยอัตโนมัติ! คุณสามารถนึกถึง red_apple เป็นวัตถุที่สืบทอดมาจาก Apple ซึ่งจะสืบทอดมาจาก Fruit ด้วยเหตุนี้ฉันจึงเปลี่ยนมาใช้ Fruit และ Apple เป็นตัวพิมพ์ใหญ่ เราแยกความแตกต่างระหว่างคลาสและวัตถุด้วยความอนุเคราะห์ของโคลนนิ่ง
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
ทำ แต่ทำไมคุณต้องใช้อย่างใดอย่างหนึ่งมากกว่าที่อื่น ๆ