Sử dụng nhiều đặc tính
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 52 trên 91.
Một trait rất hữu ích, nhưng sức mạnh thực sự đến từ việc kết hợp nhiều trait trong cùng một lớp. PHP cho phép bạn thêm bao nhiêu trait tùy ý, giúp lớp của bạn có khả năng từ nhiều nguồn cùng lúc.
Để sử dụng nhiều trait, chỉ cần liệt kê chúng, ngăn cách bằng dấu phẩy, sau từ khóa use:
<?php
trait Timestampable {
public function getTimestamp(): string {
return date('Y-m-d H:i:s');
}
}
trait Identifiable {
private string $id;
public function generateId(): void {
$this->id = uniqid('id_');
}
public function getId(): string {
return $this->id;
}
}
class Post {
use Timestampable, Identifiable;
public function __construct(public string $title) {
$this->generateId();
}
}
$post = new Post("Hello World");
echo $post->getId() . " - " . $post->getTimestamp();
Đầu ra:
id_6789abc123 - 2024-01-15 10:30:00Lớp Post hiện có các phương thức từ cả hai trait. Mỗi trait bổ sung chức năng riêng: Timestampable cung cấp các phương thức liên quan đến thời gian, còn Identifiable xử lý các ID duy nhất. Cách làm này giúp mỗi trait tập trung vào một trách nhiệm, khiến code của bạn có tính mô-đun và dễ tái sử dụng hơn.
Điều gì xảy ra khi hai trait có các phương thức trùng tên? Chúng ta sẽ tìm hiểu tình huống xung đột này trong bài học tiếp theo.
Thử thách
DễHãy xây dựng hệ thống quản lý sản phẩm kết hợp nhiều trait để sản phẩm có cả chức năng định giá và quản lý hàng tồn kho. Mỗi trait sẽ đảm nhiệm một trách nhiệm riêng, còn lớp sản phẩm sẽ có chức năng từ cả hai trait.
Bạn sẽ sắp xếp code trong ba tệp:
Priceable.php: Tạo trait có tênPriceableđể xử lý chức năng định giá. Trait cần có:- Thuộc tính riêng tư
$price(số thực) - Phương thức
setPrice(float $price)để lưu giá - Phương thức
getPrice()để trả về giá - Phương thức
getFormattedPrice()để trả về giá theo định dạng"$[price]"với 2 chữ số thập phân
- Thuộc tính riêng tư
Stockable.php: Tạo trait có tênStockableđể quản lý hàng tồn kho. Trait cần có:- Thuộc tính riêng tư
$stock(số nguyên) - Phương thức
setStock(int $quantity)để lưu số lượng hàng tồn kho - Phương thức
getStock()để trả về lượng hàng tồn kho hiện tại - Phương thức
isInStock()trả vềtruenếu lượng hàng tồn kho lớn hơn 0, nếu không thì trả vềfalse
- Thuộc tính riêng tư
main.php: Nạp cả hai tệp trait và tạo lớpProductsử dụng cả hai traitPriceablevàStockable. Dùng tính năng khai báo thuộc tính trong hàm khởi tạo để định nghĩa thuộc tính công khai$name. Hàm khởi tạo cũng cần nhận giá và số lượng hàng tồn kho, rồi dùng các phương thức của trait để thiết lập chúng.Bạn sẽ nhận ba đầu vào: tên sản phẩm, giá và số lượng hàng tồn kho. Tạo sản phẩm với các giá trị này và in ra bốn dòng:
- Tên sản phẩm
- Giá đã định dạng (dùng
getFormattedPrice()) - Số lượng hàng tồn kho
"Available"nếu còn hàng, hoặc"Out of stock"nếu hết hàng
Ví dụ này cho thấy cách nhiều trait phối hợp với nhau. Lớp Product có chức năng định giá từ một trait và chức năng quản lý hàng tồn kho từ trait còn lại. Mỗi trách nhiệm được tách biệt rõ ràng, đồng thời kết hợp lại thành một sản phẩm đầy đủ chức năng.
Tự làm thử
<?php
require_once 'Priceable.php';
require_once 'Stockable.php';
// TODO: Tạo lớp (class) Product sử dụng cả hai trait Priceable và Stockable
// Dùng constructor promotion cho thuộc tính (property) public $name
// Hàm khởi tạo (constructor) cần nhận name, price và stock, rồi dùng các phương thức (method) của trait để đặt price và stock
class Product {
// TODO: Sử dụng các trait ở đây
// TODO: Triển khai hàm khởi tạo bằng constructor promotion cho $name
// và dùng các phương thức của trait để đặt price và stock
}
// Đọc đầu vào (input)
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$stock = intval(trim(fgets(STDIN)));
// TODO: Tạo một instance Product bằng các giá trị đầu vào
// TODO: In ra tên sản phẩm
// TODO: In ra giá đã định dạng bằng getFormattedPrice()
// TODO: In ra số lượng tồn kho
// TODO: In ra "Available" nếu còn hàng, hoặc "Out of stock" nếu hết hàng
?>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 sinh2Khô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án9Đặc tính
Giới thiệu về đặc tínhSử dụng nhiều đặc tínhGiải quyết xung đột giữa các đặc tínhPhương thức trừu tượng trong đặc tínhĐặc tính và kế thừaTự luyện tập: Trình biên dịch PHP online