Глубокое погружение в клонирование объектов
Часть раздела Объектно-ориентированное программирование путешествия по 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.
Задание
ЛегкоДавайте создадим систему управления документами, демонстрирующую разницу между поверхностным и глубоким клонированием. Вы создадите документы, содержащие информацию об авторе, и обеспечите создание действительно независимой копии при клонировании документа.
Вы организуете код в трёх файлах:
Author.php: Создайте классAuthor, представляющий автора документа. Используйте promotion конструктора, чтобы определить публичные$name(строка) и$email(строка).Document.php: Создайте классDocument, содержащий автора. Подключите файл Author. Класс должен:- Использовать promotion конструктора для публичных
$title(строка) и публичного$author(Author) - Реализовать магический метод
__clone()для выполнения глубокого клонирования: когда документ клонируется, его автор также должен быть клонирован, чтобы изменения в авторе клона не влияли на original - Иметь метод
getInfo(), который returns"[title] by [author name] ([author email])"
- Использовать promotion конструктора для публичных
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: Вывести информацию клонированного документа
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Повторение — простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыПовторение — иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateМодификаторы доступа подробноГеттеры и сеттерыСокрытие информацииПродвижение конструктора (8.0)Повторение — система учёта студентов10Продвинутые концепции ООП
Композиция и наследованиеВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы2Пространства имён и автозагрузка
Введение в пространства имёнКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerПовторение — структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс и абстрактный классАннотация типов с интерфейсамиПовторение — калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeПовторение — пользовательская коллекция3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищённые свойстваСвойства только для чтения (PHP 8.1)Повторение — менеджер банковского счёта6Полиморфизм
Повторение переопределения методовПолиморфизм через интерфейсыАннотации типов и объединённые типыПозднее статическое связываниеПовторение — обработчик платежей9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования, часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн StrategyПотренируйтесь самостоятельно: Онлайн-компилятор PHP