Tìm hiểu sâu về sao chép đối tượng
Thuộc phần Lập trình hướng đối tượng trong hành trình PHP của Coddy. Bài học 61 trên 91.
Trước đó trong khóa học này, bạn đã tìm hiểu về phương thức ma thuật __clone. Giờ hãy cùng khám phá sự khác biệt quan trọng giữa sao chép nông và sao chép sâu khi đối tượng chứa các đối tượng khác làm thuộc tính.
Theo mặc định, PHP thực hiện bản sao nông khi tạo bản sao. Các giá trị vô hướng được sao chép, nhưng thuộc tính của đối tượng vẫn là tham chiếu đến cùng một đối tượng:
<?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;
Đầu ra:
Alice - LondonLưu ý rằng việc thay đổi $clone->name không ảnh hưởng đến đối tượng gốc, nhưng thay đổi thành phố trong địa chỉ thì có! Cả hai đối tượng đều dùng chung một đối tượng Address.
Để tạo một bản sao sâu, trong đó các đối tượng lồng nhau cũng được sao chép, hãy triển khai __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;
Đầu ra:
ParisGiờ đây, mỗi Person có một Address độc lập riêng. Khi các đối tượng của bạn chứa nhiều đối tượng lồng nhau, bạn phải sao chép từng đối tượng trong __clone để bản gốc và bản sao thực sự độc lập với nhau.
Thử thách
DễHãy xây dựng một hệ thống quản lý tài liệu để minh họa sự khác biệt giữa sao chép nông và sao chép sâu. Bạn sẽ tạo các tài liệu chứa thông tin tác giả và đảm bảo rằng khi sao chép một tài liệu, bản sao hoàn toàn độc lập với bản gốc.
Bạn sẽ sắp xếp code trong ba tệp:
Author.php: Tạo lớpAuthorđại diện cho tác giả của một tài liệu. Dùng cách khai báo tham số trong hàm khởi tạo để định nghĩa thuộc tính công khai$name(chuỗi) và$email(chuỗi).Document.php: Tạo lớpDocumentchứa một tác giả. Include tệp Author. Lớp này cần:- Dùng cách khai báo tham số trong hàm khởi tạo để định nghĩa thuộc tính công khai
$title(chuỗi) và$author(Author) - Triển khai phương thức ma thuật
__clone()để sao chép sâu. Khi sao chép một tài liệu, tác giả của tài liệu đó cũng cần được sao chép để các thay đổi đối với tác giả của bản sao không ảnh hưởng đến bản gốc. - Có phương thức
getInfo()trả về"[title] by [author name] ([author email])"
- Dùng cách khai báo tham số trong hàm khởi tạo để định nghĩa thuộc tính công khai
main.php: Include tệp Document. Bạn sẽ nhận ba đầu vào: tiêu đề tài liệu, tên tác giả và email tác giả.Tạo một
Documentcùng với mộtAuthorbằng các đầu vào đã cho. Sao chép tài liệu, sau đó thêm" (Copy)"vào cuối tên tác giả trong bản sao và thêm"Copy of "vào đầu tiêu đề của bản sao.In ra hai dòng:
- Thông tin của tài liệu gốc
- Thông tin của tài liệu đã sao chép
Nếu bạn triển khai sao chép sâu chính xác, tác giả của tài liệu gốc sẽ không thay đổi ngay cả sau khi bạn chỉnh sửa tác giả trong bản sao.
Thử thách này cho thấy vì sao sao chép sâu lại quan trọng. Nếu không triển khai đúng __clone(), cả hai tài liệu sẽ dùng chung một đối tượng Author, và thay đổi ở tài liệu này sẽ ảnh hưởng đến tài liệu kia.
Tự làm thử
<?php
require_once 'Document.php';
// Đọc dữ liệu đầu vào
$title = trim(fgets(STDIN));
$authorName = trim(fgets(STDIN));
$authorEmail = trim(fgets(STDIN));
// TODO: Tạo một Author bằng tên và email đã cho
// TODO: Tạo một Document bằng tiêu đề đã cho và Author
// TODO: Clone tài liệu
// TODO: Sửa tên tác giả của bản clone bằng cách thêm " (Copy)" vào cuối
// TODO: Sửa tiêu đề của bản clone bằng cách thêm "Copy of " vào đầu
// TODO: In ra thông tin của tài liệu gốc
// TODO: In ra thông tin của tài liệu đã clone
?>Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về OOP
Tệp bên ngoàiGiới thiệu về OOPLớp và đối tượngTừ khóa $thisPhương thứcThuộc tínhHàm khởi tạo (__construct)Hàm hủy (__destruct)Ôn tập: máy tính đơn giản4Kế thừa
Kế thừa cơ bảnTừ khóa parent::Ghi đè phương thứcTừ khóa finalLớp trừu tượngÔn tập: hệ thống phân cấp nhân viên7Đóng gói
Công khai, được bảo vệ, riêng tưTìm hiểu sâu về bộ điều chỉnh quyền truy cậpGetter và setterẨn thông tinRút gọn hàm khởi tạo (8.0)Ôn tập: hệ thống hồ sơ học sinh10Các khái niệm OOP nâng cao
Kết hợp hay kế thừaTiêm phụ thuộcLớp ẩn danhKiểu liệt kê (PHP 8.1)Fiber (PHP 8.1)Tìm hiểu sâu về sao chép đối tượngBộ sinh và bộ lặp2Không gian tên và tự động nạp
Giới thiệu về không gian tênTừ khóa useChuẩn tự động nạp PSR-4Trình tự động nạp của ComposerÔn tập: dự án có tổ chức5Giao diện và hợp đồng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnGiao diện và lớp trừu tượngGợi ý kiểu với giao diệnÔn tập: Máy tính hình học8Phương thức ma thuật
Giới thiệu về phương thức ma thuật__toString và __debugInfo__get, __set, __isset, __unset__call và __callStatic__clone và sao chép đối tượng__serialize và __unserializeÔn tập: Bộ sưu tập tùy chỉnh3Thuộc tính của lớp
Thuộc tính instance và staticHằng số trong lớpPhương thức và thuộc tính staticThuộc tính private và protectedThuộc tính chỉ đọc (PHP 8.1)Ôn tập: Trình quản lý tài khoản ngân hàng6Đa hình
Xem lại việc ghi đè phương thứcĐa hình qua giao diệnGợi ý kiểu và kiểu hợpLiên kết tĩnh muộnÔn tập: bộ xử lý thanh toánTự luyện tập: Trình biên dịch PHP online