Menu
CoddyTech

Giao diện và lớp trừu 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 30 trên 91.

Giờ bạn đã học về cả interface lẫn lớp trừu tượng, hãy cùng tìm hiểu khi nào nên dùng từng loại. Dù trông có vẻ giống nhau, chúng có mục đích và khả năng riêng biệt.

Tính năng Giao diện Lớp trừu tượng
Phương thức Chỉ có chữ ký phương thức Có cả phương thức trừu tượng và phương thức cụ thể
Thuộc tính Chỉ có hằng số Có thể có thuộc tính
Kế thừa đa lớp Một lớp có thể triển khai nhiều giao diện Một lớp chỉ có thể kế thừa một lớp
Hàm khởi tạo Có thể khai báo chữ ký __construct (không có phần thân) Có thể định nghĩa hàm khởi tạo có phần thân

Dùng interface khi bạn muốn định nghĩa một hợp đồng mà các lớp không liên quan có thể đáp ứng. Ví dụ, cả Printer và Logger đều có thể triển khai một interface Writable, dù chúng không có tổ tiên chung.

Dùng một lớp trừu tượng khi bạn có các lớp liên quan dùng chung đoạn code. Nếu cả lớp Dog và Cat đều cần cùng một cách triển khai phương thức eat(), lớp trừu tượng Animal cho phép bạn viết đoạn code đó một lần.

Bạn cũng có thể kết hợp cả hai cách. Một lớp có thể kế thừa một lớp trừu tượng đồng thời triển khai nhiều interface:

<?php
abstract class Animal {
    protected $name;
    
    public function eat() {
        return $this->name . " is eating";
    }
}

interface Swimmable {
    public function swim();
}

class Duck extends Animal implements Swimmable {
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function swim() {
        return $this->name . " is swimming";
    }
}

Điểm chính: Hãy chọn interface để xác định các khả năng dùng chung giữa những lớp không liên quan. Hãy chọn lớp trừu tượng khi bạn cần chia sẻ code giữa những lớp có liên quan.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống phương tiện để minh họa khi nào nên dùng lớp trừu tượng thay vì giao diện, cũng như cách kết hợp hiệu quả cả hai cách tiếp cận.

Bạn sẽ tạo một hệ thống trong đó các phương tiện dùng chung thuộc tính thông qua kế thừa, đồng thời có thể có thêm năng lực nhờ giao diện. Điều này phản ánh các quyết định thiết kế trong thực tế: ô tô “là một” phương tiện (kế thừa), nhưng cũng “có khả năng” được tiếp nhiên liệu (giao diện).

Sắp xếp code của bạn trong bốn tệp:

  • Refuelable.php: Định nghĩa giao diện Refuelable với một khai báo phương thức duy nhất: refuel($amount). Năng lực này có thể áp dụng cho bất kỳ lớp nào cần được tiếp nhiên liệu, bất kể hệ thống phân cấp kế thừa của lớp đó.
  • Vehicle.php: Định nghĩa lớp trừu tượng Vehicle, cung cấp chức năng dùng chung cho mọi phương tiện. Lớp này cần có thuộc tính $brand được bảo vệ và thuộc tính $fuelLevel được bảo vệ (bắt đầu từ 0). Hàm khởi tạo nhận tên hãng. Thêm phương thức cụ thể getBrand() trả về hãng, phương thức cụ thể getFuelLevel() trả về mức nhiên liệu hiện tại và phương thức trừu tượng drive() mà các lớp con phải triển khai.
  • Car.php: Tạo lớp Car kế thừa Vehicle và triển khai Refuelable. Thêm cả hai tệp bắt buộc ở đầu tệp. Hàm khởi tạo nhận hãng và truyền giá trị đó cho hàm khởi tạo của lớp cha. Triển khai drive() để trả về "[brand] car is driving". Triển khai refuel($amount) để cộng lượng nhiên liệu vào mức nhiên liệu và trả về "[brand] refueled to [fuelLevel] liters".
  • main.php: Thêm tệp Car. Bạn sẽ nhận hai đầu vào: tên hãng và lượng nhiên liệu (chuyển thành số nguyên). Tạo một Car với tên hãng đó. In kết quả của drive() trên dòng đầu tiên, sau đó in kết quả của refuel() với lượng nhiên liệu trên dòng thứ hai.

Thử thách này làm rõ điểm khác biệt chính: lớp trừu tượng Vehicle chia sẻ code và thiết lập mối quan hệ “là một”, trong khi giao diện Refuelable bổ sung một năng lực có thể áp dụng cho những lớp không liên quan khác (như máy phát điện hoặc thuyền) mà không cần chúng phải là phương tiện.

Tự làm thử

<?php
require_once 'Car.php';

// Đọc đầu vào
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));

// TODO: Tạo một Car với thương hiệu đã nhập

// TODO: In kết quả của drive() trên dòng đầu tiên

// TODO: In kết quả của refuel() với lượng nhiên liệu đã nhập trên dòng thứ hai
?>
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