![Прототип](/images/patterns/cards/prototype-mini.png?id=bc3046bb39ff36574c08d49839fd1c8e)
Прототип на Ruby
Прототип — это порождающий паттерн, который позволяет копировать объекты любой сложности без привязки к их конкретным классам.
Все классы—Прототипы имеют общий интерфейс. Поэтому вы можете копировать объекты, не обращая внимания на их конкретные типы и всегда быть уверены, что получите точную копию. Клонирование совершается самим объектом-прототипом, что позволяет ему скопировать значения всех полей, даже приватных.
Сложность:
Популярность:
Применимость: Паттерн Прототип реализован в базовой библиотеке Ruby посредством методов dup
или clone
.
Признаки применения паттерна: Прототип легко определяется в коде по наличию методов clone
, copy
и прочих.
Концептуальный пример
Этот пример показывает структуру паттерна Прототип, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.rb: Пример структуры паттерна
# Пример класса, имеющего возможность клонирования. Мы посмотрим как происходит
# клонирование значений полей разных типов.
class Prototype
attr_accessor :primitive, :component, :circular_reference
def initialize
@primitive = nil
@component = nil
@circular_reference = nil
end
# @return [Prototype]
def clone
@component = deep_copy(@component)
# Клонирование объекта, который имеет вложенный объект с обратной ссылкой,
# требует специального подхода. После завершения клонирования вложенный
# объект должен указывать на клонированный объект, а не на исходный объект.
@circular_reference = deep_copy(@circular_reference)
@circular_reference.prototype = self
deep_copy(self)
end
# Нередко метод deep_copy использует хак «маршалинг», чтобы создать глубокую
# копию объекта. Однако это медленное и неэффективно, поэтому в реальных
# приложениях используйте для этой задачи соответствующий пакет.
#
# @param [Object] object
private def deep_copy(object)
Marshal.load(Marshal.dump(object))
end
end
class ComponentWithBackReference
attr_accessor :prototype
# @param [Prototype] prototype
def initialize(prototype)
@prototype = prototype
end
end
# Клиентский код.
p1 = Prototype.new
p1.primitive = 245
p1.component = Time.now
p1.circular_reference = ComponentWithBackReference.new(p1)
p2 = p1.clone
if p1.primitive == p2.primitive
puts 'Primitive field values have been carried over to a clone. Yay!'
else
puts 'Primitive field values have not been copied. Booo!'
end
if p1.component.equal?(p2.component)
puts 'Simple component has not been cloned. Booo!'
else
puts 'Simple component has been cloned. Yay!'
end
if p1.circular_reference.equal?(p2.circular_reference)
puts 'Component with back reference has not been cloned. Booo!'
else
puts 'Component with back reference has been cloned. Yay!'
end
if p1.circular_reference.prototype.equal?(p2.circular_reference.prototype)
print 'Component with back reference is linked to original object. Booo!'
else
print 'Component with back reference is linked to the clone. Yay!'
end
output.txt: Результат выполнения
Primitive field values have been carried over to a clone. Yay!
Simple component has been cloned. Yay!
Component with back reference has been cloned. Yay!
Component with back reference is linked to the clone. Yay!