Menu
Coddy logo textTech

Глубокое погружение в клонирование объектов

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 61 из 91.

Ранее в этом курсе вы узнали о магическом методе __clone. Теперь давайте рассмотрим важное различие между поверхностным и глубоким клонированием, когда объекты содержат другие объекты в качестве свойств.

По умолчанию PHP выполняет поверхностное копирование при клонировании. Скалярные значения дублируются, но свойства объектов остаются ссылками на те же объекты:

<?php
class Address {
    public function __construct(public string $city) {}
}

class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;

$clone->name = "Bob";
$clone->address->city = "London";

echo $original->name . " - " . $original->address->city;

Вывод:

Alice - London

Обратите внимание, что изменение $clone->name не повлияло на original, но изменение города address повлияло! Оба объекта используют один и тот же экземпляр Address.

Чтобы создать глубокую копию, в которой также клонируются вложенные объекты, Implement __clone:

<?php
class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
    
    public function __clone(): void {
        $this->address = clone $this->address;
    }
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->address->city = "London";

echo $original->address->city;

Вывод:

Paris

Теперь каждый Person имеет собственный независимый Address. Когда ваши объекты содержат несколько вложенных объектов, вы должны клонировать каждый из них в __clone, чтобы добиться полной независимости между original и Copy.

challenge icon

Задание

Легко

Давайте создадим систему управления документами, демонстрирующую разницу между поверхностным и глубоким клонированием. Вы создадите документы, содержащие информацию об авторе, и обеспечите создание действительно независимой копии при клонировании документа.

Вы организуете код в трёх файлах:

  • Author.php: Создайте класс Author, представляющий автора документа. Используйте promotion конструктора, чтобы определить публичные $name (строка) и $email (строка).
  • Document.php: Создайте класс Document, содержащий автора. Подключите файл Author. Класс должен:
    • Использовать promotion конструктора для публичных $title (строка) и публичного $author (Author)
    • Реализовать магический метод __clone() для выполнения глубокого клонирования: когда документ клонируется, его автор также должен быть клонирован, чтобы изменения в авторе клона не влияли на original
    • Иметь метод getInfo(), который returns "[title] by [author name] ([author email])"
  • main.php: Подключите файл Document. Вы получите три входных значения: заголовок документа, имя автора и электронную почту автора.

    Создайте Document с помощью Author, используя provided inputs. Клонируйте документ, затем измените имя автора клона, appending к нему " (Copy)", и измените заголовок клона, prepending к нему "Copy of ".

    Выведите две строки:

    • Информацию original документа
    • Информацию клонированного документа

    Если глубокое клонирование реализовано правильно, автор original документа должен остаться неизменным даже после изменения автора клона.

Этот challenge демонстрирует, почему глубокое клонирование имеет значение: без правильной реализации __clone() оба документа совместно использовали бы один и тот же объект Author, и изменение одного влияло бы на другой.

Попробуйте сами

<?php

require_once 'Document.php';

// Чтение входных данных
$title = trim(fgets(STDIN));
$authorName = trim(fgets(STDIN));
$authorEmail = trim(fgets(STDIN));

// TODO: Создать Author с предоставленными именем и email

// TODO: Создать Document с предоставленным title и Author

// TODO: Клонировать документ

// TODO: Изменить имя автора клона, добавив к нему " (Copy)"

// TODO: Изменить title клона, добавив в начало "Copy of "

// TODO: Вывести информацию исходного документа

// TODO: Вывести информацию клонированного документа

?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP