Busqueda local

Loading

domingo, 13 de marzo de 2011

Copiar Objetos

Antes de las clases, copiar una variable era tan simple como usar el signo de igual entre las dos variables, con lo cual el valor almacenado en la variable que se encontraba a la derecha del símbolo se le asignaba a la variable que se encuentra a la izquierda del símbolo. Todo era muy simple. A continuación comentare los mecanismos relacionados con la copia de objetos, a manera de preámbulo para un posterior Post, donde hablare del Patrón Prototype.
 

Como se copia un Objeto?

Existen tres formas de crear una copia de una instancia:
  • Copia de referencia (Default)
  • Copia superficial
  • Copia profunda.
Copia de referencia
Si hiciéramos con dos objetos lo mismo que hicimos con las variables del párrafo anterior, el resultado sería que ambas variables serian el mismo objeto. Cualquier cambio que le hiciéramos a uno afectaría al otro. La razón es, por que las variables que estamos manipulando en realidad son apuntadores al lugar en memoria donde se encuentra el objeto. Por lo que al hacer la asignación, ambas variables tendrían el mismo valor, el apuntador. Este es el proceso default.
Copia Superficial
La copia superficial se logra mediante el llamado al método MemberwiseClone, mismo que crea una copia superficial; puesto que crea una nueva instancia, con un pequeño detalle. Las propiedades que sean de tipo valor se copiaran, pero aquellas que sean de referencia, como una clase, se copiara dicha referencia, no los objetos. Dando como resultado la misma situación de la que platicamos en el segundo párrafo de este post.
El método MemberwiseClone pertenece a la clase Object, sin embargo al ser un método Protected solo podrá ser llamado por aquellos objetos que deriven de la clase personalizada de la cual estemos. Por ejemplo, tenemos dos clases Persona y Animal, ambas derivan de Object, in embargo no podemos llamar al método MemberwiseClone de la clase animal desde dentro de la clase Persona.
La pregunta sería: Entonces por que existe? La razón es muy simple, es muy rápida y no depende del tamaño de los datos.
Copia Profunda
El .NET nos ofrece una interface que define el métodos Clone que se debe codificar para realizar una copia o Clon, o sea una nueva instancia con los mismos valores de una instancia que previamente existía. En este caso, existen algunas consideraciones, con respecto las propiedades de referencia.
Podemos cometer el error de escribir código dentro del método Clone de la clase contenedora, para realizar el clone de las clases referenciadas, lo cual representa una clara violación al Principio de Encapsulamiento. Esto quiere decir que para realizar una copia a profundidad, es necesario que nuestras clases estén diseñadas desde el principio para ser “Cloneables”, lo que quiere decir que cada clase que esté incluida como referencia dentro de la clase contenedora, deberá exponer un método para realizar el método Clone.
 

Manos a la obra

Ya lo sé ha sido, mucho rollo, como decimos en México, y a ustedes les interesa ver ejemplo, sin embargo en algunas ocasiones la teoría es importante, no?
Ejemplo:
Requerimientos
Imaginemos que en nuestro proyecto, fuera necesario realizar una copia de una clase Reactivo, que a su vez contiene una colección de Respuestas. Este sería un escenario en el cual deberíamos implementar una copia profunda; para lograr esto se requiere que nuestras clases sean diseñadas desde el principio para poder soportar esta copia profunda.
Lo primero es implementar la interface ICloneable es la clase Respuesta:
clon1
Después en la Clase Reactivo, aun que si nuestra clase pertenece a un Model del EF, la conversión requiere de una extensión, para que nos ayude con la conversión de las entidades, además de que será necesario hacer un CAST explicito, para poder llenar la Colección de entidades y de esa forma regresar una copia de las respuestas. Quiero hacer la aclaración de que esta no es la única forma posible, y podría existir alguna más simple, sin embargo me decidí por esta ya que sirve para reforzar dos temas de los que he hablado previamente en este Blog, la instrucción YIELD  y los métodos de Extensión.
Nuestro método de extensión quedaría de esta forma:
clon2
Observen la forma en que se llama al método Clone de la clase antes de realizar el Return Yield.
Y ahora nuestra Clase Reactivo implementando la interface ICloneable, así como usando nuestro método de extensión para obtener una copia de cada respuesta:
clon3
Nuestro código de prueba quedaría de esta manera:
clon4
El poder del código solo es completo, si tenemos el conocimiento de como usarlo.

No hay comentarios:

Publicar un comentario