post_ico4

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"

?>
Co sądzisz o wpisie?
BeżnadziejnySłabyŚredniDobryBardzo dobry (Brak ocen, bądź pierwszy!)
Loading...
  • łukasz

    W tytule napisałeś „deep clone” co sugeruje opisanie tego w poście. Jeśli tak to gdzie to jest opisane, bo choć post krótki to nigdzie nie widzę zagnieżdżonych obiektów. Szkoda, bo chętnie bym poczytał.
    > Jeżeli chcemy skopiować obiekt musimy użyć metody __clone().
    Musimy a jednak nie używasz je w swoim kodzie.

  • Łukasz Socha

    Faktycznie, zbyt uprościłem przykład. Wpis zaktualizowany, dzięki.

  • Adrian

    __clone() to metoda magiczna nie służąca do kopiowania obiektu (tak wynika z Twojego tekstu) tylko metoda uruchamiana w momencie kiedy obiekt jest kopiowany – w niej dopiero robisz kopię obiektu z własności „zawod”. Doprecyzowałbym, że kopiowania używa się konstrukcji językowej „clone”, opisujesz także, że używasz __clone() w odniesieniu do tworzenia kopii obiektu zmiennej „osoba” co też nie jest prawdą patrząc na Twój kod.

    Tak po za tym, blog całkiem fajny od dawna leży w RSSach.

    Pozdrawiam,
    Adrian

    • Faktycznie błędnie to sformułowałem. Poprawione, dzięki.