Kết hợp hay kế thừa
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 56 trên 91.
Trong chương trước, bạn đã học rằng trait cung cấp khả năng “có một”, còn kế thừa thiết lập mối quan hệ “là một”. Composition phát triển thêm khái niệm “có một” bằng cách tạo các đối tượng phức tạp từ những đối tượng đơn giản hơn và lưu chúng dưới dạng thuộc tính thay vì kế thừa từ chúng.
Với tính kế thừa, Car có thể mở rộng Vehicle. Với tính kết hợp, Car chứa một đối tượng Engine:
<?php
class Engine {
public function start(): string {
return "Engine started";
}
}
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine();
}
public function start(): string {
return $this->engine->start();
}
}
$car = new Car();
echo $car->start();
Đầu ra:
Engine startedLớp Car không kế thừa từ Engine, mà sở hữu một đối tượng thuộc lớp này. Cách tiếp cận này có nhiều ưu điểm: bạn có thể thay động cơ bằng một cách triển khai khác, các lớp vẫn ít phụ thuộc lẫn nhau và bạn tránh được hệ thống phân cấp cứng nhắc do kế thừa nhiều tầng tạo ra.
Một nguyên tắc phổ biến trong lập trình hướng đối tượng là “ưu tiên kết hợp hơn kế thừa.” Kế thừa phù hợp với các hệ phân cấp kiểu thực sự (Dog thực sự là một Animal), nhưng kết hợp phát huy hiệu quả khi bạn ghép chức năng từ các phần độc lập. Bạn có thể kết hợp nhiều đối tượng, thay thế chúng khi chương trình đang chạy và kiểm thử riêng từng thành phần.
Bài học tiếp theo sẽ tìm hiểu về tiêm phụ thuộc (dependency injection), dựa trực tiếp trên tính kết hợp bằng cách kiểm soát cách các đối tượng nhận những phần phụ thuộc của chúng.
Thử thách
DễHãy xây dựng một hệ thống máy tính bằng cách kết hợp các thành phần, trong đó Computer được tạo thành từ các đối tượng thành phần riêng biệt thay vì kế thừa từ chúng. Điều này minh họa cách kết hợp cho phép bạn xây dựng các đối tượng phức tạp từ những phần độc lập, đơn giản hơn.
Bạn sẽ sắp xếp code trong ba tệp:
Processor.php: Tạo lớpProcessorđại diện cho CPU. Dùng tính năng tự động khai báo thuộc tính trong hàm khởi tạo để định nghĩa thuộc tính riêng$model(chuỗi) và$cores(số nguyên). Thêm phương thứcgetSpecs()trả về"[model] ([cores] cores)".Memory.php: Tạo lớpMemoryđại diện cho RAM. Dùng tính năng tự động khai báo thuộc tính trong hàm khởi tạo để định nghĩa thuộc tính riêng$size(số nguyên biểu thị dung lượng GB). Thêm phương thứcgetCapacity()trả về"[size]GB RAM".main.php: Nạp cả hai tệp thành phần và tạo lớpComputersử dụng cách kết hợp thành phần. LớpComputercần:- Lưu một đối tượng
Processorvà một đối tượngMemorylàm thuộc tính riêng - Nhận mẫu bộ xử lý, số lõi và dung lượng bộ nhớ trong hàm khởi tạo
- Tạo các đối tượng thành phần bên trong lớp khi khởi tạo
- Có phương thức
describe()trả về"Computer with [processor specs] and [memory capacity]"
Bạn sẽ nhận ba đầu vào: mẫu bộ xử lý, số lõi và dung lượng bộ nhớ tính bằng GB. Tạo một
Computertheo các thông số này và in ra kết quả khi gọidescribe().- Lưu một đối tượng
Hãy chú ý rằng Computer không mở rộng Processor hay Memory. Nó chứa chúng. Mối quan hệ “có” này thông qua cách kết hợp giúp mỗi thành phần hoạt động độc lập và có thể tái sử dụng, đồng thời cho phép Computer ủy quyền cho các thành phần khi cần.
Tự làm thử
<?php
require_once 'Processor.php';
require_once 'Memory.php';
class Computer
{
// TODO: Định nghĩa các thuộc tính riêng tư để lưu các đối tượng Processor và Memory
// TODO: Hàm khởi tạo nhận mẫu bộ xử lý, số lõi và dung lượng bộ nhớ
// và tự tạo các đối tượng thành phần
public function __construct()
{
// TODO: Tạo và lưu các đối tượng Processor và Memory
}
// TODO: Viết phương thức describe() trả về
// "Computer with [processor specs] and [memory capacity]"
public function describe(): string
{
// TODO: Gọi các đối tượng thành phần và trả về chuỗi đã định dạng
}
}
// Đọc đầu vào
$processorModel = trim(fgets(STDIN));
$cores = intval(fgets(STDIN));
$memorySize = intval(fgets(STDIN));
// TODO: Tạo một Computer theo thông số đã cho và in ra kết quả của describe()
?>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