Deep clone, czyli kopiowanie obiektów

Kopiowanie obiektów może się wydawać dla większości wręcz trywialne. Jednak, dla początkujących w programowaniu obiektowym może to nie być takie oczywiste.

O co chodzi?

Żeby lepiej zrozumieć problem przeanalizujmy przykład:

<?php  

class Zawod{
    private $nazwa;

    function __construct($nazwa) {
        $this->nazwa=$nazwa;
    }
    
    public function setNazwa($nazwa) {
        $this->nazwa=$nazwa;
    }
    public function getNazwa() {
        return $this->nazwa;
    }
}

class Osoba {

    private $imie;
    private $zawod;

    function __construct($imie, $nazwa) {
        $this->imie = $imie;
        $this->zawod=new Zawod($nazwa);
    }
    public function setImie($imie) {
        $this->imie = $imie;
    }
    public function getImie() {
        return $this->imie;
    }
    public function setZawod($zawod) {
        $this->zawod->setNazwa($zawod);
    }
    public function getZawod() {
        return $this->zawod->getNazwa();
    }

}

$osoba=new Osoba('Ania', 'sekretarka');
$osoba2 = $osoba;
echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Ania - sekretarka"
echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Ania - sekretarka"
$osoba->setImie('Tomek');
$osoba->setZawod('kierowca');
echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Tomek - kierowca"
echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Tomek - kierowca"

?>

Do 45 linii wszystko wydaje się oczywiste. Jednak, w ostatniej linii dostajemy „niespodziewany” wynik – zmiana imienia i zawodu w obiekcie $osoba powoduje także zmianę w $osoba2. Dlaczego tak się dzieje? W linii 43 tak naprawdę nie robimy kopii obiektu jak to się dzieje w typach prostych (liczby, stringi itp.), tylko tworzymy nową referencję (odnośnik na odpowiedni adres pamięci, więcej informacji na Wikipedii) do obiektu stworzonego za pomocą zmiennej $osoba. Jeżeli chcemy skopiować obiekt musimy użyć clone oraz stworzyć magiczną metodę __clone() w klasie Osoba, którą wykorzystamy do sklonowania właściwości obiektu.

<?php  

class Zawod{
    private $nazwa;

    function __construct($nazwa) {
        $this->nazwa=$nazwa;
    }
    
    public function setNazwa($nazwa) {
        $this->nazwa=$nazwa;
    }
    public function getNazwa() {
        return $this->nazwa;
    }
}

class Osoba {

    private $imie;
    private $zawod;

    function __construct($imie, $nazwa) {
        $this->imie = $imie;
        $this->zawod=new Zawod($nazwa);
    }
    public function setImie($imie) {
        $this->imie = $imie;
    }
    public function getImie() {
        return $this->imie;
    }
    public function setZawod($zawod) {
        $this->zawod->setNazwa($zawod);
    }
    public function getZawod() {
        return $this->zawod->getNazwa();
    }
    public function __clone() {
        $this->zawod = clone $this->zawod;
    }

}

$osoba=new Osoba('Ania', 'sekretarka');
$osoba2 = clone $osoba;
echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Ania - sekretarka"
echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Ania - sekretarka"
$osoba->setImie('Tomek');
$osoba->setZawod('kierowca');
$osoba2->setZawod('kierowniczka');
echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Tomek - kierowca"
echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Ania - kierowniczka"

?>
Print Friendly, PDF & Email