Extensiones al core de Ruby, copia profunda de objetos con el api de marshalling (serializando, vamos)
Publicado por el Martes, 23 de Octubre de 2007
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:
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.
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.

Tu comentario