Menu
CoddyTech

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 started

Lớ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.

challenge icon

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ớp Processor đạ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ức getSpecs() trả về "[model] ([cores] cores)".
  • Memory.php: Tạo lớp Memory đạ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ức getCapacity() trả về "[size]GB RAM".
  • main.php: Nạp cả hai tệp thành phần và tạo lớp Computer sử dụng cách kết hợp thành phần. Lớp Computer cần:
    • Lưu một đối tượng Processor và một đối tượng Memory là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 Computer theo các thông số này và in ra kết quả khi gọi describe().

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()

?>
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch PHP online