Полное руководство по созданию копии объекта в Ruby: Часть II

В этой статье мы собираемся изучить следующие темы:

  • метод #initialize_copy
  • Marshal класс
  • метод #deep_dup в Rails

Взгляните на Часть I (3 минуты чтения)

Прежде, чем начать

Позвольте мне представить здесь платформу, которая помогла мне изучить большую часть моих знаний о Ruby. Действительно, Pluralsight - потрясающая платформа.

С 50+ курсами, которые охватывают различные темы по Ruby и Ruby on Rails, это лучший способ поднять свои знания на новый уровень!

Попробуйте бесплатно 👇😉



Спасибо за уделенное время!

Вступление

По умолчанию Ruby не поддерживает глубокое копирование.

В действительности он обрабатывает только поверхностное копирование с помощью методов Object#clone и Object#dup.

Но что, если нам все же нужно сделать полную копию объекта?

В этой статье мы подробно расскажем о трех способах создания полной копии объекта в Ruby.

Метод #initialize_copy

Хук #initialize_copy может использоваться для взаимодействия со только что созданной копией данного объекта.

Мы можем настроить этот хук, чтобы получить глубокую копию.

Во-первых, давайте вспомним, как добиться неглубокой копии в Ruby.

Проблема в том, что у khabib и tony_fergusson один и тот же пояс - UFC Lightweight Champion.

Это означает, что если khabib изменит атрибут title.name, то это изменение будет распространено на атрибут tony_fergusson.title.name, поскольку они используют один и тот же Title экземпляр.

Итак, давайте реализуем Fighter#initialize_copy, чтобы сделать полную копию khabib

В приведенном выше примере мы определяем метод Fighter#initialize_copy, который назначает мелкую копию original_fighter.title (khabib) self.title (tony_fergusson).

Итак, если мы сравним object_id из двух заголовков, мы увидим, что теперь это два разных экземпляра Title.

Это прекрасно работает!

Но проблема здесь в том, что мы «настраиваем» метод, который вызывается в контексте неглубокой копии (через #dup и #clone), чтобы обрабатывать глубокую копию.

Конечно, это не серьезная проблема!

Но давайте посмотрим на другие альтернативы.

Marshal класс

Object Marshalling - это концепция форматирования представления объекта в памяти, чтобы сделать его пригодным для хранения и десериализации.

В принципе, для данного объекта:

  • мы сериализуем все значения его атрибутов
  • мы храним сериализацию (например, в файле или в переменной)
  • мы десериализуем его в любое время и получаем новый экземпляр сериализованного объекта с теми же значениями.

В Ruby логика Object Marshalling в основном реализована в классе Marshal.

Давайте посмотрим на следующий пример, чтобы подробно узнать, как работает этот класс.

В приведенном выше примере мы видим, что переменная jd содержит экземпляр User.

Затем мы сериализуем этот экземпляр с помощью метода Marshal.dump.

Этот метод возвращает строку, которая представляет собой сериализацию представления памяти переменной jd.

Наконец, мы десериализуем эту строку с помощью метода Marshal.load.

Этот метод возвращает новый экземпляр User, который содержит те же значения, что и jd.

Как мы видим, jd и other_jd - это два разных экземпляра User.

Теперь давайте посмотрим, как обработать глубокую копию с помощью Object Marshalling.

Для этого давайте реорганизуем пример классов Fighter и Title.

Здесь мы сериализуем khabib экземпляр Fighter с помощью метода Marshal.dump.

Затем мы десериализуем его с помощью метода Marshal.load и сохраняем результат в переменной tony_ferguson.

Как видим, мы достигаем того же результата, что и с методом #initialize_copy.

Фактически, атрибут title khabib и tony_fergusson - это два разных экземпляра Title.

Обработка глубокой копии с помощью маршалинга объектов может быть связана с двумя основными проблемами:

  • это может стать очень медленной операцией в масштабе
  • он не полностью работает со сложными объектами.

В противном случае это довольно эффективный способ добиться глубокого копирования в Ruby.

Теперь давайте посмотрим, что предлагает Ruby on Rails для обработки глубокого копирования.

Обратите внимание, что я раскрою Object Marshalling в Ruby в другой статье.

Метод #deep_dup в Rails

Платформа Ruby on Rails предоставляет метод Object#deep_dup, который позволяет вам создавать глубокую копию заданного объекта.

Это решение реализовано в ~ 30 LOC

1- Метод Object#duplicable? возвращает true.

Это означает, что экземпляр, содержащий класс Object в своей цепочке предков, имеет право на глубокое копирование.

Есть только несколько классов, которые нельзя «дублировать»:

NilClass FalseClass TrueClass Symbol Numeric BigDecimal Method Complex Rational

2- Метод Object#deep_dup возвращает значение, возвращаемое вызовом метода dup или self, если объект не имеет права на глубокое копирование.

3- Метод Array#deep_dup map через вызывающий массив и вызывает #deep_dup для каждого элемента массива.

4- Hash#deep_dup метод dup вызывающий хеш и выполняет итерацию по вызывающему хешу.

Затем для каждой пары он проверяет, является ли ключ замороженным объектом.

Если это так, то только value равно #deep_dup.

В противном случае key и value равны #deep_dup.

Существует также набор #initialize_copy методов, определенных для обработки сложных объектов (в ActiveRecord::Relation и т. Д.).

Вуаля!

Спасибо, что нашли время прочитать этот пост :-)

Не стесняйтесь 👏 и поделитесь этой статьей, если она была для вас полезна. 🚀

Вот ссылка на мою последнюю статью:

The Complete Guide to Create a Copy of an Object in Ruby: Part I