Otra forma de construir objetos es via los métodos
dup y clone.
Estos métodos crean un objeto que es una copia del del objeto que recibe el mensaje:
>> a = "hello" => "hello" >> b = a.dup => "hello" >> c = a.clone => "hello" >> [a.__id__, b.__id__, c.__id__] => [2150357800, 2150329860, 2150304120]
clone hace una copia mas parecida que dup. Por ejemplo,
conserva el estatus de freeze del original, cosa que no hace dup:
>> a = "hello" => "hello" >> a.freeze => "hello" >> a << " world" TypeError: can't modify frozen string >> b = a.clone => "hello" >> b << " world" TypeError: can't modify frozen string >> c = a.dup => "hello" >> c << " world" => "hello world"
clone incluso preserva los singleton methods, esto es, preserva los métodos individuales
del objeto. Sin embargo, dup no lo hace.
Un singleton method es un método x que se añade a un objeto obj
específico. A partir del momento en el que se añade, ese objeto obj
responde el mensaje x, cosa que no hacen los otros métodos de su clase.
Sigue un ejemplo:
~/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules$ cat -n singletonmethandclone.rb
1 obj = "some object"
2 def obj.printme
3 puts "***<#{self}>***"
4 end
5
6 obj.printme
7
8 b = obj.dup
9 c = obj.clone
10
11 c.printme
12 b.printme
En la línea 2 añadimos el método printtime únicamente al objeto obj. El objeto clonado
c también puede hacer printtime. No así la copia b obtenida por duplicación:
~/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules$ ruby singletonmethandclone.rb ***<some object>*** ***<some object>*** singletonmethandclone.rb:12: undefined method `printme' for "some object":String (NoMethodError)
clone y dup copian las variables de instancia / atributos del objeto original al objeto final
sólo copian las referencias a los valores de esas variables no copian los valores reales: sólo hacen una copia superficial.
El siguiente ejemplo ilustra este punto:
~/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules$ cat -n dupclone1.rb
1 class Point
2 attr_accessor :coords
3 def initialize(* coords)
4 @coords = coords
5 end
6 end
7
8 if __FILE__ == $0
9 puts "dup"
10 p = Point.new(1, 2, 3)
11 q = p.dup
12 puts p.inspect
13 puts q.inspect
14
15 q.coords[1] = 4
16 puts p.inspect
17 puts q.inspect
18
19 puts "clone"
20 p = Point.new(1, 2, 3)
21 q = p.clone
22 puts p.inspect
23 puts q.inspect
24
25 q.coords[1] = 4
26 puts p.inspect
27 puts q.inspect
28 end
La ejecución nos muestra que la copia es superficial:
~/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules$ ruby dupclone1.rb dup #<Point:0x100169008 @coords=[1, 2, 3]> #<Point:0x100168fe0 @coords=[1, 2, 3]> #<Point:0x100169008 @coords=[1, 4, 3]> #<Point:0x100168fe0 @coords=[1, 4, 3]> clone #<Point:0x100168c20 @coords=[1, 2, 3]> #<Point:0x100168bf8 @coords=[1, 2, 3]> #<Point:0x100168c20 @coords=[1, 4, 3]> #<Point:0x100168bf8 @coords=[1, 4, 3]>Lo que se necesita es una copia profunda.
initialize_copy,
los métodos clone y dup lo invocarán en el objeto copia.
Al método initialize_copy se le pasa el objeto original como argumento.
El valor retornado por initialize_copy es ignorado:
~/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules$ cat -n dupclone2.rb
1 class Point
2 attr_accessor :coords
3 def initialize(* coords)
4 @coords = coords
5 end
6
7 def initialize_copy(orig)
8 @coords = orig.coords.dup
9 # or better
10 # @coords = @coords.dup
11 end
12 end
13
14 if __FILE__ == $0
15 puts "dup"
16 p = Point.new(1, 2, 3)
17 q = p.dup
18 puts p.inspect
19 puts "q = p.dup = "+q.inspect
20
21 q.coords[1] = 4
22 puts "Changed q[1] to 4 and p has "+p.inspect
23 puts "Changed q[1] to 4 and q has "+q.inspect
24
25 puts "clone"
26 p = Point.new(1, 2, 3)
27 q = p.clone
28 puts p.inspect
29 puts "q = p.clone = "+q.inspect
30
31 q.coords[1] = 4
32 puts "Changed q[1] to 4 and p has "+p.inspect
33 puts "Changed q[1] to 4 and q has "+q.inspect
34 end
La ejecución produce la siguiente salida:
~/rubytesting/TheRubyProgrammingLanguage/Chapter7ClassesAndModules$ ruby dupclone2.rb dup #<Point:0x100168900 @coords=[1, 2, 3]> q = p.dup = #<Point:0x1001688d8 @coords=[1, 2, 3]> Changed q[1] to 4 and p has #<Point:0x100168900 @coords=[1, 2, 3]> Changed q[1] to 4 and q has #<Point:0x1001688d8 @coords=[1, 4, 3]> clone #<Point:0x1001683d8 @coords=[1, 2, 3]> q = p.clone = #<Point:0x1001683b0 @coords=[1, 2, 3]> Changed q[1] to 4 and p has #<Point:0x1001683d8 @coords=[1, 2, 3]> Changed q[1] to 4 and q has #<Point:0x1001683b0 @coords=[1, 4, 3]>
Casiano Rodriguez León 2015-01-07