オブジェクトのクローンを徹底解説
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 61/91。
このコースの前半で、__clone magic method について学びました。ここでは、オブジェクトがプロパティとして他のオブジェクトを含む場合の、shallow cloning と deep cloning の重要な違いについて見ていきましょう。
デフォルトでは、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 には影響しませんでしたが、住所の city を変更すると影響しました。両方のオブジェクトが同じ Address インスタンスを共有しています。
ネストされたオブジェクトもクローンされるdeep コピーを作成するには、__cloneを Implement します。
<?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 を持ちます。オブジェクトに複数のネストされたオブジェクトが含まれている場合、original とコピーの間で真の独立性を実現するには、__clone 内でそれぞれを clone する必要があります。
チャレンジ
簡単浅い cloning と deep cloning の違いを示す document management system を構築しましょう。author 情報を含む document を作成し、document の cloning によって本当に独立した Copy が作成されることを確認します。
コードを3つのファイルに整理します。
Author.php:document の author を表すAuthorclass を Create します。constructor promotion を使用して、public$name(string)と public$email(string)を define します。Document.php:author を含むDocumentclass を Create します。Author file を Include します。class では次のことを行います。- public
$title(string)と public$author(Author)に constructor promotion を使用する __clone()magic method を Implement して deep cloning を実行する。document が cloned されたとき、その author も cloned されるため、clone の author への変更が original に影響しないgetInfo()method を持ち、"[title] by [author name] ([author email])"を returns する
- public
main.php:Document file を Include します。3つの inputs、つまり document の title、author の name、author の email を受け取ります。provided inputs を使用して
AuthorとともにDocumentを Create します。document を clone し、clone の author name に" (Copy)"を appending して Modify し、clone の title の先頭に"Copy of "を prepending して Modify します。2行を Print します。
- original document の info
- cloned document の info
deep cloning が正しく Implement されていれば、clone の author を Modify した後も、original document の author は変更されない should です。
この challenge は、deep cloning が重要である理由を示します。__clone() の適切な Implement がなければ、両方の document が同じ Author object を共有し、一方を変更するともう一方にも影響します。
自分で試してみよう
<?php
require_once 'Document.php';
// 入力を読み取る
$title = trim(fgets(STDIN));
$authorName = trim(fgets(STDIN));
$authorEmail = trim(fgets(STDIN));
// TODO: 提供された名前とメールで Author を作成する
// TODO: 提供されたタイトルと Author で Document を作成する
// TODO: ドキュメントをクローンする
// TODO: クローンの著者名の末尾に " (Copy)" を追加して変更する
// TODO: クローンのタイトルの先頭に "Copy of " を追加して変更する
// TODO: 元のドキュメントの情報を出力する
// TODO: クローンしたドキュメントの情報を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: PHPオンラインコンパイラ