
Полное руководство по созданию копии объекта в 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