4Trabes Historias de una empresa en 100 metros cuadrados

El blog de Trabe Soluciones

Extensiones al core de Ruby, copia profunda de objetos con el API de marshalling (serializando, vamos)

| | Comentarios

Una necesidad típica a la hora de programar es copiar o clonar objetos. Los métodos de Ruby Object#clone y Object#dup realizan copias superficiales (shallow_), es decir, si el objeto contiene referencias a otros objetos se duplican las referencias pero no los objetos apuntados. Si la copia fuese profunda (_deep), también se duplicarían los objetos referenciados. Ejemplo:

1
2
3
4
5
6
7
h = { 1 => ['a','b'], 2 => ['c'] }
h1 = h.clone
h2 = h.deep_clone
h1[1].shift    # => "a"
h   # =>  {1=>["b"], 2=>["c"]}
h1  # =>  {1=>["b"], 2=>["c"]}
h2  # =>  {1=>["a", "b"], 2=>["c"]}

Lo primero que se nos puede ocurrir es crear un método deep_clone específico para cada objeto (o incluso uno más genérico que, mediante introspección del objeto supiese que copiar). Ésto es tedioso y tiene sus problemas (básicamente el meanejo de referencias cruzadas), además, eso no es DRY. Por fortuna Ruby tiene soporte para serialización (módulo Marshal). El truco es sencillo: serializo, deserializo.

1
2
3
4
5
6
class Object
  def deep_clone
    # Abracadabra !!!
    Marshal::load(Marshal::dump(self))
  end
end

Sencillo, bello, y todas esas cosas que tanto gustan de Ruby.

A esta técnica sólo podemos ponerle dos pegas: 1) si una parte de la clase no es serializable no podremos copiarla, y 2) es cierto que una implementación ad hoc de copia puede ser más rápida (pero yo, al menos, estoy dispuesto a perder esa velocidad).

PD: Por cierto, esta técnica tan sencilla es trasladable directamente a cualquier otro lenguaje/plataforma con soporte para serialización: Java, .NET, etc.

Lo sentimos, pero los comentarios están cerrados

No sabes como de útil me ha sido esto :)

Me alegro, me alegro. Para eso estamos