Clonar objetos

En PHP 5 cuando asignamos un identificador de objeto a otro ($obj2 = $obj1) lo que hacemos es copiar la referencia al mismo. Por esta razón vamos a necesitar de algún tipo de función de clonado o constructor copia.

Una copia de un objeto es creada usando la palabra clone la cual llama el método **__clone()** del objeto que no podremos llamar de forma directa. Si el método __clone() no esta definido se llamará a uno por defecto.

La forma de clonar objetos es la siguiente:

$copiaObjeto = clone $objeto;

Cuando un objeto es clonado, PHP 5 realiza una copia superficial (si tenemos un objeto cuyo atributo es un objeto la copia apuntará al mismo objeto) de todos los atributos del objeto. Por tanto cualquier atributo que sea referencia a otra variable, permanecerá siendo referencia.

Si deseamos hacer una copia en profundidad deberemos implementarla en el método __clone().

Veamos el siguiente ejemplo donde tenemos una agregación.

class A
{
   public $Dato;
   public $DatoClaseB;
   public function __construct($dato, $datoClaseB) {
      $this->Dato = $dato;
      $this->DatoClaseB = $datoClaseB;
   }
   public function __toString() {
      return $this->Dato . ' ' . $this->DatoClaseB . '<br>';
   }
   public function __clone() {
      // Realizamos la copia en profundidad de la agregación.
      $this->DatoClaseB = clone $this->DatoClaseB;
   }
}
class B // No implementamos __clone y dejamos copia superficial
{
   public $Dato;
   public function __construct($dato) {
      $this->Dato = $dato;
   }
   public function __toString() {
      return strval($this->Dato);
   }
}
$instanciaB = new B(100);
$instanciaA = new A(200, $instanciaB);
$referenciaA = $instanciaA;
$copiaDeA = clone $instanciaA;
$referenciaA->Dato = 414;
$referenciaA->DatoClaseB->Dato = 141;
$copiaDeA->Dato = 313;
$copiaDeA->DatoClaseB->Dato = 131;
echo $instanciaA . $referenciaA . $copiaDeA;

Resultado

414 141
414 141
313 131

Si necesitamos un comportamiento no predeterminado para clone, tendremos que reemplazar el método \_\_clone ( ) en la clase base, método similar a un constructor o un destructor, ya que no se invoca directamente. Se invoca cuando se utiliza la palabra clave clone como indicamos en este apartado. Tras ellos, dentro del método \_\_clone ( ) podemos definir exactamente el tipo de comportamiento de copia que queramos.

Lo mejor de __clone( ) es que se invocará tras crearse una copia exacta por medio del comportamiento predeterminado, por lo que sólo tendremos que cambiar lo que queramos modificar.

La funcionalidad más habitual que se añade a __clone( ) es código para garantizar que los atributos de la clase que se procesan como referencias se copian correctamente. Si queremos clonar una clase que contiene una referencia a un objeto, probablemente esperaremos una segunda copia de dicho objeto en lugar de una segunda referencia al mismo, por lo que tendría sentido añadirlo a __clone( ).

También podemos optar por no cambiar nada y realizar otra acción diferente, como por ejemplo actualizar un registro de base de datos subyacente relacionado con la clase.

**Ejemplo de referencia, copia superficial y copia en profundidad**

Tenemos una clase **Moto** y una clase **Rueda**. La clase moto tendrá dos atributos: peso y un conjunto de objetos de tipo Ruedas (un array de ruedas)

En este ejemplo nos creamos un objeto **moto1** que será una instancia de la clase Moto. Tendrá un peso de 100 kg y dos instancias de la clase Rueda1 y Rueda2

Veamos un ejemplo gráfico:

El objeto moto1 tiene un peso de 1 y un array con dos objetos de tipo Rueda

**Asignación normal de referencias**

Ahora creamos una nueva variable $moto2 y la asignamos a $moto1. La variable $moto2 apunta a la misma posición de memoria que $moto1.

Creamos un nuevo objeto $moto2 y le asignamos la misma referencia que $moto1

**Copia superficial usando un clone normal**

Se crea una nuevo objeto con copias superficiales pero no las referencias que se mantienen como vemos en la imagen siguiente:

Clonado superficial del objeto $moto1

**Copia en profundidad**

La solución está en implementar el método __clone() dentro de la clase Moto. En dicho método crearemos las instancias de los objetos referenciados por la clase base.

En la siguiente imagen podemos verlo de forma gráfica:

Implementación del método __clone ( para realizar una copia en profundidad)

La solución está en implementar el método __clone() dentro de la clase Moto. En dicho método crearemos las instancias de los objetos referenciados por la clase base.

results matching ""

    No results matching ""