средства копирования объектов в IO

Nov 30, 2007 23:11

Исключая специфику для разных типов данных, в стандарт включено три метода:

  • clone - создаёт новый пустой объект и записывает «себя» (получателя сообщения «clone») новому объекту в прототипы. Типичное использование - для создания классов:

    MyClass := Object clone do( slot ::= value action := method( doSomething ) )
    И объектов из классов: myObject := MyClass clone setSlot(1) action
  • cloneWithoutInit может быть нужен в специальных ситуациях. Например, когда ты хочешь сделаь свою версию clone, которая будет по-другому вызывать init. (А, да, и я не буду говорить, что делает этот метод. Догадаетесь;).

  • shallowCopy копирует все слоты «себя» (получателя сообщения «shallowCopy») в новый объект, полученный клонированием Object. Сделан на сях, но его можно сделать и в IO (чуть более медленный): Object shallowCopy := method( result := Object clone; self slotNames foreach(name, result setSlot(name, self getSlot(name))); result ). Он будет плохо себя вести для встроенных типов (для которых есть скрытые данные, доступные только через методы - для чисел, списков, строк, ...), точнее сказать, и вовсе не будет работать. А нужен он, например, в случаях, когда в цепочку наследования нужно встроить объект из другой цепочки. Например, это полезно, если хочется сделать библиотеку миксинов, среди которых есть такие, которые перекрывают методы из Object, и из этой библиотеки нужно воспользоваться сразу несколькими. (Не зная о существовании этого метода, я написал ровно такой же). Таким образом, типичный для меня шаблон использования этого метода, такой:

    MyClass := Object clone( setProto(MixinOne shallowCopy setProto(proto)) setProto(MixinTwo shallowCopy setProto(proto)) setProto(MixinThree shallowCopy setProto(proto)) appendProto(NonCriticalMixin) appendProto(OtherJunkMixin) slot ::= value action := method( doSomething ) )
    (И если у меня таких случаев будет больше одного, то, конечно, я сделаю и способ более красивой записи того же самого: Object insertProto := method(o, self setProto(o shallowCopy setProto(self proto))...)

    И ещё я замечу, что то, что shallowCopy не копирует списка прототипов - это явный недостаток (моя версия на одну строчку сложнее описанной и список прототипов копирует). Вполне допустимо и полезно, чтобы такого рода миксины могли указывать для себя прототипы кроме Object, которые бы тогда переносились в дерево наследования автоматически, и для того, чтобы это работало, достаточно следовать простой договорённости, что миксины наследуют всё, что им требуется, через второй и далее прототипы.
  • duplicate создаёт новый объект, в который копирует побайтно содержимое «себя». А так как на сишном уровне структура объекта является, по сути, указателем на комплект указателей, то это означает, что скопирован будет этот самый указатель. Внешний. Вероятно, это баг и на самом деле авторы хотели сделать чуть-чуть более глубокую копию, но на деле это выглядит как два объекта с (NB) одинаковыми адресами в памяти, но разными записями в сборщике мусора (плюс некоторое количество пляски вокруг сборщика мусора при создании, чтобы он не обижался на такие клоны). Задокументирован метод так: «Creates a new copy of the receiver, including its proto list.»

PS. Забыл очень важное замечание сделать. Для встроенных типов (числа, строки, списки и пр.) clone следует трактовать как операцию копирования.

И ещё забыл сказать, что при клонировании (и только при нём) на свежесозданный объект вызывается метод init, если таковой имеется где-нибудь в прототипах.

io

Previous post Next post
Up