Menu
CoddyTech

Đa hình qua giao diện

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 34 trên 91.

Trong bài học trước, bạn đã tìm hiểu về tính đa hình thông qua kế thừa, trong đó các lớp con ghi đè phương thức của lớp cha. Giao diện là một cách mạnh mẽ khác để đạt được tính đa hình, đặc biệt khi làm việc với các lớp không liên quan nhưng có chung hành vi.

Với giao diện, tính đa hình xuất phát từ chính hợp đồng. Mọi lớp triển khai một giao diện đều đảm bảo có một số phương thức nhất định, bất kể hệ thống phân cấp kế thừa của lớp đó:

<?php
interface Notifiable {
    public function send(string $message);
}

class EmailNotifier implements Notifiable {
    public function send(string $message) {
        return "Email: " . $message;
    }
}

class SMSNotifier implements Notifiable {
    public function send(string $message) {
        return "SMS: " . $message;
    }
}

class PushNotifier implements Notifiable {
    public function send(string $message) {
        return "Push: " . $message;
    }
}

function notify(Notifiable $notifier, string $message) {
    return $notifier->send($message);
}

echo notify(new EmailNotifier(), "Hello") . "\n";
echo notify(new SMSNotifier(), "Hello") . "\n";
echo notify(new PushNotifier(), "Hello");

Đầu ra:

Email: Hello
SMS: Hello
Push: Hello

Hàm notify() hoạt động với mọi đối tượng Notifiable. Các lớp này không có chung lớp cha nào, chúng hoàn toàn không liên quan đến nhau, ngoại trừ việc cùng triển khai một giao diện. Đây là ưu điểm chính so với tính đa hình dựa trên kế thừa: bạn có thể nhóm các lớp không liên quan với nhau dựa trên khả năng của chúng thay vì nguồn gốc kế thừa.

Điểm chính: Tính đa hình dựa trên giao diện cho phép dùng các lớp không liên quan thay thế cho nhau dựa trên hành vi chung, giúp code linh hoạt hơn và ít phụ thuộc vào các cách triển khai cụ thể.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống lưu trữ minh họa tính đa hình dựa trên giao diện. Bạn sẽ tạo các hệ thống lưu trữ khác nhau, gồm hệ thống tệp và cơ sở dữ liệu, không có điểm chung nào ngoài việc cả hai đều triển khai cùng một giao diện lưu trữ. Một hàm duy nhất sẽ hoạt động với mọi kiểu lưu trữ, cho thấy giao diện cho phép các lớp hoàn toàn không liên quan có tính đa hình như thế nào.

Bạn sẽ sắp xếp code trong bốn tệp:

  • Storable.php: Định nghĩa giao diện Storable với hai chữ ký phương thức: save($key, $data) và retrieve($key). Hợp đồng này đảm bảo mọi hệ thống lưu trữ đều có thể lưu và truy xuất dữ liệu, bất kể cách triển khai bên trong.
  • FileStorage.php: Tạo lớp FileStorage triển khai Storable. Nhúng tệp giao diện. Lớp này cần có thuộc tính riêng tư $directory, được thiết lập thông qua hàm khởi tạo. Triển khai save($key, $data) để trả về "Saving '[data]' to file [directory]/[key].txt". Triển khai retrieve($key) để trả về "Reading from file [directory]/[key].txt".
  • DatabaseStorage.php: Tạo lớp DatabaseStorage cũng triển khai Storable. Nhúng tệp giao diện. Lớp này cần có thuộc tính riêng tư $tableName, được thiết lập thông qua hàm khởi tạo. Triển khai save($key, $data) để trả về "Inserting '[data]' into table [tableName] with key [key]". Triển khai retrieve($key) để trả về "Selecting from table [tableName] where key = [key]".
  • main.php: Nhúng cả hai tệp lưu trữ. Tạo hàm có tên storeData, nhận một tham số kiểu Storable, một khóa và dữ liệu. Hàm này cần trả về kết quả của lời gọi save() với khóa và dữ liệu đó. Tạo một hàm khác có tên fetchData, nhận một tham số kiểu Storable và một khóa, rồi trả về kết quả của lời gọi retrieve().

Bạn sẽ nhận bốn đầu vào: đường dẫn thư mục, tên bảng, khóa và dữ liệu cần lưu. Tạo một đối tượng FileStorage với đường dẫn thư mục và một đối tượng DatabaseStorage với tên bảng. Dùng hàm storeData() để lưu dữ liệu trước bằng hệ thống lưu trữ tệp, sau đó bằng hệ thống lưu trữ cơ sở dữ liệu. In mỗi kết quả trên một dòng riêng. Tiếp đó, dùng fetchData() để truy xuất dữ liệu từ hệ thống lưu trữ tệp và in kết quả đó.

Hãy chú ý cách storeData() và fetchData() hoạt động giống hệt nhau với cả hai kiểu lưu trữ. Chúng chỉ cần đối tượng triển khai Storable. Hệ thống tệp và cơ sở dữ liệu có cách triển khai nội bộ hoàn toàn khác nhau, nhưng vẫn có thể thay thế cho nhau nhờ hợp đồng giao diện.

Tự làm thử

<?php
require_once 'FileStorage.php';
require_once 'DatabaseStorage.php';

// Đọc đầu vào
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));

// TODO: Tạo hàm (function) storeData nhận một Storable, khóa và dữ liệu
// Hàm cần trả về kết quả khi gọi save() với khóa và dữ liệu

// TODO: Tạo hàm fetchData nhận một Storable và khóa
// Hàm cần trả về kết quả khi gọi retrieve() với khóa

// TODO: Tạo một instance FileStorage với thư mục

// TODO: Tạo một instance DatabaseStorage với tên bảng

// TODO: Dùng storeData() để lưu dữ liệu vào tệp, rồi in ra kết quả

// TODO: Dùng storeData() để lưu dữ liệu vào cơ sở dữ liệu, rồi in ra kết quả

// TODO: Dùng fetchData() để lấy dữ liệu từ tệp, rồi in ra kết quả
?>
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