Ruby: #clone срещу #dup

Същността на плиткото копие

Според Ruby-doc, и #clone и #dup могат да бъдат използвани за създаване на плитко копие на обект, който преминава само по един сложен слой, което означава, че променливите от инстанцията на obj са копирани, но не и обектите, към които се позовават. Всички те биха споделяли едни и същи атрибути; промяната на едната би довела до промяна на друга. Въпреки това, #clone прави две неща, различни от #dup:

1) #clone поддържа замразеното или опетнено състояние на obj. като има предвид, че #dup би го променил на опетнен.

a = [1, 2, 3, 4, 5]
a.freeze
a_clone = a.clone
a_clone << 6 # => не може да модифицира замразения масив
a_dup = a.dup
a_dup << 6 # => [1, 2, 3, 4, 5, 6]

В повечето обектно-ориентирани езици за програмиране обектите обикновено са сменяеми (или опетнени) с ограничен набор от изключения. В Ruby мутабилността е свойство на екземпляр, а не на цял клас. Всеки случай може да стане неизменна, като се обадите на #freeze. Превръща обект в константа и улеснява прилагането на капсулирането. Следователно, ако част от състоянието на обекта е замразено, методите на accessor могат да върнат този неизменен обект на външни обаждащи се без страх, че тези обаждащи се могат да променят състоянието на обекта.

клас Риби
  attr_accessor: име
  @@ all = []
  def инициализиране (име)
    @ име = име
    @@ всички << себе си
  край
  def self.all
    @@ all.dup.freeze
  край
край
риба = Fish.all
fish << Human.new ("John") # => не може да модифицира замразения масив

Ruby също автоматично замразява хеш ключа, ако ключът е низ. По този начин, ако първоначалният низ се промени по-късно, той няма да повлияе на хеш ключа.

fish = {"Dory" => {: name => "Dory",: type => "Regal Blue Tang"},
         „Nemo“ => {: name => „Nemo“,: type => „Clownfish“}
}
fish.keys.first.frozen? # => вярно
fish.keys.first.replace ("Луда синя риба") # => не може да модифицира замразения низ

2) #clone копира всички единични методи на обект, но #dup не поддържа това.

клас Риби
  attr_accessor: име
  def инициализиране (име)
    @ име = име
  край
край
dory = Fish.new ("Dory")
def dory.whale_talk
  "Mmmmoooooowaaaaah ..."
край
dory_clone = dory.clone
dory_clone.whale_talk # => "Mmmmoooooowaaaaah ..."
dory_dup = dory.dup
dory_dup.whale_talk # => неопределен метод `whale_talk 'за # 
Като цяло, клонирането и дупването могат да имат различна семантика в низходящите класове. Докато клонингът се използва за дублиране на обект, включително неговото вътрешно състояние, dup обикновено използва класа на обекта на потомък, за да създаде новия екземпляр.

По същество, #clone и #dup произвеждат плитки копия на обект. #clone копира всичко: вътрешно състояние, сингълтон методи и др. #dup копира само съдържанието на обекта (плюс опетненото състояние).

Дълбокото копие, от друга страна, се използва в сценарии, при които се създава ново копие (клонинг) без позоваване на оригиналните данни (за повече информация за разликата между двете може да намерите в моя друг пост)

*** Внимавайте ***

В Rails 4.0, #clone и #dup се държат малко по-различно. #clone е точното копие на обект ActiveRecord. Те споделят всички атрибути, дори и първичния ключ.

Идентичен за метода на клониране на Руби. Това е „плитко“ копие. Внимавайте, че атрибутите ви не се копират. Това означава, че промяната на атрибутите на клона ще модифицира оригинала, тъй като и двамата ще сочат към един и същи хеш на атрибути. Ако се нуждаете от копие на хеша на вашите атрибути, моля, използвайте метода #dup.

Въпреки че #dup все още се счита за плитко копие, той няма пряка връзка с оригинала; и по този начин промяната на атрибутите на дупиран обект няма да доведе до промяна в първоначалния.

Дупираните обекти нямат зададен идентификатор и се третират като нови записи. Обърнете внимание, че това е „плитко“ копие, тъй като копира само атрибутите на обекта, а не неговите асоциации. Степента на „дълбокото“ копие е специфична за приложението и затова се оставя на приложението да се реализира според нуждите му.
[1] pry> original = User.find (1)
=> # <Потребителски идентификационен номер: 1, първо име: „Джон“, фамилия: „Смит“, имейл: нула, създадено_от: „2016–08–07 15:17:03“, актуализирано_ат: „2016–08–07 16: 01:43 ">
[2] pry> original.clone
=> # <Идентификационен номер на потребителя: 1, име_файл: „Джон“, фамилия: „Смит“, имейл: нула, създадено_от: „2016–08–07 15:17:03“, актуализирано_ат: „2016–08–07 16: 01:43 ">
[3] pry> original.dup
=> # <Идентификационен номер на потребителя: nil, име_файл: „Джон“, фамилия: „Смит“, имейл: nil, създаден_ат: нул, актуализиран_ат: нул>