stringtranslate.com

Copia de objetos

En programación orientada a objetos , la copia de objetos es el acto de crear e inicializar un nuevo objeto en función del estado de un objeto existente. Las diversas formas de implementar la copia tienen implicaciones que un programador debe comprender para poder escribir un programa de computadora que sea correcto y eficaz.

La copia permite utilizar e incluso modificar el estado emergente del objeto original (representado por su estado interno) sin afectar el objeto original.

Estrategias

Generalmente, un objeto se asemeja a un concepto monolítico pero tiene una estructura interna que son datos compuestos : un árbol de estado. Se han desarrollado varias estrategias para copiar este estado interno según las necesidades del programa y el costo del tiempo de ejecución.

Las primeras que se analizan son las copias superficiales y profundas, cuya terminología se remonta a Smalltalk -80. [1]

Una distinción similar se aplica al comparar objetos en busca de igualdad . Para que dos objetos sean iguales, su estado debe ser el mismo de manera significativa. Dos objetos podrían considerarse iguales si sus campos son iguales sin atravesar subobjetos (superficiales). O tal vez se consideren iguales sólo si el estado es igual en todo el árbol de objetos (profundo). [ se necesita aclaración ]

Si dos variables contienen el mismo valor de referencia, entonces claramente se refieren al mismo objeto, que es incluso más específico que igual.

Copia de referencia

Incluso más superficial que la copia superficial, copiar una referencia es una forma de copiar un objeto. Esta estrategia se emplea comúnmente al pasar un objeto a un método. La referencia se pasa por valor: una copia del valor de referencia (probablemente una dirección).

Copia superficial

La copia superficial implica crear un objeto nuevo no inicializado, B, y copiar cada valor de campo del original, A. [2] [3] [4] Debido a este procedimiento, esto también se conoce como copia campo por campo . [5] [6] [7] copia campo por campo , o copia de campo . [8] Si el valor del campo es un tipo primitivo (como int), el valor se copia de manera que los cambios en el valor en B no afecten el valor en A. Si el valor del campo es una referencia a un objeto (por ejemplo, una dirección de memoria) la referencia se copia, por lo tanto se hace referencia al mismo objeto que A. Cambiar el estado del objeto interno afecta el estado emergente tanto de A como de B, ya que los objetos son compartidos. En un lenguaje sin tipos primitivos (donde todo es un objeto), todos los campos de la copia hacen referencia a los mismos objetos que los campos del original.

Una copia superficial suele ser relativamente sencilla de implementar y computacionalmente barata de realizar. Por lo general, se puede implementar simplemente copiando un bloque de memoria contiguo.

copia profunda

La copia profunda implica copiar el estado de todos los objetos subordinados: desreferenciar recursivamente las referencias de objetos en cada nivel del árbol que es el estado del objeto original y crear nuevos objetos y copiar campos. Una modificación del objeto original o copiado, incluidos sus objetos internos, no afecta al otro ya que no comparten contenido.

Híbrido

En casos más complejos, algunos campos de una copia deberían tener valores compartidos con el objeto original (como en una copia superficial), correspondientes a una relación de asociación ; y algunos campos deben tener copias (como en una copia profunda), correspondientes a una relación de agregación . En estos casos, generalmente se requiere una implementación personalizada de copia; Este problema y solución datan de Smalltalk-80. [9] Alternativamente, los campos se pueden marcar como que requieren una copia superficial o una copia profunda, y las operaciones de copia se generan automáticamente (de la misma manera para las operaciones de comparación). [10] Sin embargo, esto no está implementado en la mayoría de los lenguajes orientados a objetos, aunque hay soporte parcial en Eiffel. [10]

copia perezosa

La copia diferida, relacionada con la copia en escritura , es una implementación de una copia profunda. Al copiar inicialmente un objeto, se realiza una copia superficial relativamente rápida. También se utiliza un contador para rastrear cuántos objetos comparten los datos. Cuando el programa quiere modificar un objeto, puede determinar si los datos se comparten (examinando el contador) y puede realizar una copia profunda si es necesario.

La copia diferida proporciona la semántica de una copia profunda, pero aprovecha la velocidad de una copia superficial cuando es posible. La desventaja son unos costes básicos bastante elevados pero constantes debido al contador. Las referencias circulares pueden causar problemas.

Ejemplos

Generalmente, un lenguaje de programación orientado a objetos proporciona una forma de copiar un objeto. Un programador debe definir cómo se copia un objeto personalizado, del mismo modo que debe definir si dos objetos son iguales, comparables, etc.

Algunos lenguajes admiten una de las estrategias superficiales o profundas, definiendo una operación de copia u operaciones superficiales y profundas separadas. [10] Muchos idiomas proporcionan algún comportamiento predeterminado.

Java

En Java, siempre se accede a un objeto de forma indirecta, a través de una referencia . Un objeto nunca se crea implícitamente, sino que siempre se pasa o asigna mediante una variable de referencia.

Los parámetros se pasan por valor, sin embargo, lo que se pasa es el valor de la referencia. [11]

La máquina virtual Java gestiona la recolección de basura para que los objetos se limpien cuando ya no sean accesibles.

El lenguaje no proporciona una forma automática de copiar un objeto.

La copia generalmente se realiza mediante un método clone() . Este método generalmente llama al método clone() de su clase principal para obtener una copia y luego realiza cualquier procedimiento de copia personalizado. Finalmente, esto llega al método clone() del objeto superior ( Object), que crea una nueva instancia de la misma clase que el objeto y copia todos los campos en la nueva instancia (una copia superficial). Si se utiliza este método, la clase debe implementar la Cloneableinterfaz; de lo contrario, se generará una "Excepción de clonación no admitida". Después de obtener una copia de la clase principal, el método clone() propio de una clase puede proporcionar capacidad de clonación personalizada, como copia profunda (es decir, duplicar algunas de las estructuras a las que hace referencia el objeto) o darle a la nueva instancia una nueva ID única.

El tipo de retorno de clone() es Object, pero los implementadores de un método de clonación podrían escribir el tipo del objeto que se está clonando debido al soporte de Java para tipos de retorno covariantes . Una ventaja de usar clone() es que, dado que es un método reemplazable , podemos llamar a clone() en cualquier objeto, y utilizará el método clone() de su clase, sin que el código de llamada necesite saber cuál es esa clase. (que sería necesario con un constructor de copias).

Una desventaja es que a menudo no se puede acceder al método clone() en un tipo abstracto. La mayoría de las interfaces y clases abstractas en Java no especifican un método público clone(). Por lo tanto, a menudo la única forma de utilizar el método clone() es si se conoce la clase de un objeto, lo que va en contra del principio de abstracción de utilizar el tipo más genérico posible. Por ejemplo, si uno tiene una referencia de Lista en Java, no puede invocar clone() en esa referencia porque List no especifica ningún método público de clonación(). Las implementaciones de List como Array List y Linked List generalmente tienen métodos clone(), pero es inconveniente y una mala abstracción llevar consigo el tipo de clase de un objeto.

Otra forma de copiar objetos en Java es serializarlos a través de la Serializableinterfaz. Esto generalmente se usa con fines de persistencia y protocolo de conexión , pero crea copias de objetos y, a diferencia de la clonación, una copia profunda que maneja con elegancia gráficos cíclicos de objetos está disponible con un mínimo esfuerzo por parte de un programador.

Ambos métodos adolecen de un problema notable: el constructor no se utiliza para objetos copiados mediante clonación o serialización. Esto puede provocar errores con datos inicializados incorrectamente, impide el uso de finalcampos de miembros y dificulta el mantenimiento. Algunas utilidades intentan superar estos problemas mediante el uso de la reflexión para realizar copias profundas de objetos, como la biblioteca de clonación profunda. [12]

eiffel

Se puede acceder a los objetos de tiempo de ejecución en Eiffel ya sea indirectamente a través de referencias o como objetos expandidos cuyos campos están incrustados dentro de los objetos que los usan. Es decir, los campos de un objeto se almacenan externa o internamente .

La clase Eiffel ANYcontiene funciones para la copia y clonación superficial y profunda de objetos. Todas las clases de Eiffel heredan de ANY, por lo que estas funciones están disponibles en todas las clases y son aplicables tanto a objetos de referencia como expandidos.

La copycaracterística efectúa una copia superficial, campo por campo, de un objeto a otro. En este caso no se crea ningún objeto nuevo. Si yse copiaron en x, los mismos objetos a los que se hace referencia yantes de la aplicación de copy, también se hará referencia a ellos xdespués de que se complete la copyfunción.

Para efectuar la creación de un nuevo objeto que sea un duplicado superficial de , se utiliza yesta característica . twinEn este caso, se crea un nuevo objeto con sus campos idénticos a los de la fuente.

La característica twinse basa en la característica copy, que se puede redefinir en descendientes de ANY, si es necesario. El resultado de twines del tipo anclado like Current.

La copia profunda y la creación de gemelos profundos se pueden realizar utilizando las funciones deep_copyy deep_twin, nuevamente heredadas de la clase ANY. Estas características tienen el potencial de crear muchos objetos nuevos, porque duplican todos los objetos en una estructura de objetos completa. Debido a que se crean nuevos objetos duplicados en lugar de simplemente copiar referencias a objetos existentes, las operaciones profundas se convertirán en una fuente de problemas de rendimiento más fácilmente que las operaciones superficiales.

C#

En C# , en lugar de utilizar la interfaz ICloneable, se puede utilizar un método de extensión genérico para crear una copia profunda mediante la reflexión. Esto tiene dos ventajas: en primer lugar, proporciona la flexibilidad de copiar cada objeto sin tener que especificar cada propiedad y variable que se copiará manualmente. En segundo lugar, debido a que el tipo es genérico, el compilador garantiza que el objeto de destino y el objeto de origen tengan el mismo tipo. [ cita necesaria ]

C objetivo

En Objective-C , los métodos copyy mutableCopyson heredados por todos los objetos y están destinados a realizar copias; este último es para crear un tipo mutable del objeto original. Estos métodos, a su vez, llaman a los métodos copyWithZoney mutableCopyWithZone, respectivamente, para realizar la copia. Un objeto debe implementar el copyWithZonemétodo correspondiente para ser copiable. [ cita necesaria ]

OCaml

En OCaml , la función de biblioteca Oo.copy realiza una copia superficial de un objeto.

Pitón

En Python , el módulo de copia de la biblioteca proporciona copia superficial y copia profunda de objetos a través de las funciones copy()y deepcopy(), respectivamente. [13] Los programadores pueden definir métodos especiales __copy__()y __deepcopy__()en un objeto para proporcionar una implementación de copia personalizada.

Rubí

En Ruby , todos los objetos heredan dos métodos para realizar copias superficiales, clonar y duplicar. Los dos métodos se diferencian en que clonecopia el estado contaminado, el estado congelado y cualquier método singleton que pueda tener un objeto, mientras que dupcopia solo su estado contaminado. Se pueden lograr copias profundas volcando y cargando el flujo de bytes de un objeto o la serialización YAML.[1] Alternativamente, puedes usar la gema deep_dive para hacer una copia profunda controlada de tus gráficos de objetos. [2]

perla

En Perl , las estructuras anidadas se almacenan mediante el uso de referencias, por lo que un desarrollador puede recorrer toda la estructura y volver a hacer referencia a los datos o usar la dclone()función del módulo Almacenable.

Vba

En VBA , una asignación de variables de tipo Objectes una copia superficial, una asignación para todos los demás tipos (tipos numéricos, cadenas, tipos definidos por el usuario, matrices) es una copia profunda. Entonces, la palabra clave Setpara una tarea indica una copia superficial y la palabra clave (opcional) Letindica una copia profunda. No existe un método integrado para copias profundas de objetos en VBA. [ cita necesaria ]

Ver también

Notas

  1. ^ Goldberg y Robson 1983, págs. 97–99. "Hay dos formas de hacer copias de un objeto. La distinción es si los valores de las variables del objeto se copian o no. Si los valores no se copian, entonces se comparten ( shallowCopy); si los valores se copian, entonces se comparten no compartido ( deepCopy)."
  2. ^ "Explicación de copia superficial versus profunda de C ++".
  3. ^ "Explicación de copia superficial y profunda de .NET".
  4. ^ "Explicación genérica de copia superficial versus profunda". Archivado desde el original el 4 de marzo de 2016 . Consultado el 10 de abril de 2013 .
  5. ^ Core Java: Fundamentos, Volumen 1, p. 295
  6. ^ Java efectivo , segunda edición, p. 54
  7. ^ "¿Qué es esta copia campo por campo realizada por Object.clone()? "
  8. ^ "Josh Bloch sobre diseño: una conversación con el autor eficaz de Java, Josh Bloch", por Bill Venners, JavaWorld , 4 de enero de 2002, p. 13
  9. ^ Goldberg y Robson 1983, pág. 97. "La implementación predeterminada de copyes shallowCopy. En las subclases en las que la copia debe dar como resultado una combinación especial de variables compartidas y no compartidas, generalmente se vuelve a implementar el método asociado con la copia, en lugar del método asociado con shallowCopyo deepCopy".
  10. ^ abc Grogono y Sakkinen 2000.
  11. ^ "Pasar información a un método o constructor" . Consultado el 8 de octubre de 2013 .
  12. ^ Biblioteca de clonación profunda de Java
  13. ^ Módulo de copia de Python

Referencias