Mẫu tổ hợp
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 80 trên 91.
Mẫu Composite là một mẫu thiết kế cấu trúc, cho phép bạn ghép các đối tượng thành cấu trúc cây rồi làm việc với những cấu trúc này như thể chúng là các đối tượng riêng lẻ. Mẫu này rất phù hợp để biểu diễn hệ thống phân cấp bộ phận và tổng thể, khi bạn muốn xử lý đồng nhất các đối tượng riêng lẻ và nhóm đối tượng.
Hãy xem xét một hệ thống tệp. Một thư mục có thể chứa các tệp và thư mục khác.
Khi tính tổng kích thước, bạn muốn gọi cùng một phương thức cho cả tệp và thư mục, còn thư mục sẽ cộng đệ quy kích thước nội dung bên trong. Mẫu Composite giúp việc này diễn ra liền mạch bằng cách cung cấp cùng một giao diện cho cả nút lá (tệp) và nút tổng hợp (thư mục).
<?php
interface FileSystemItem {
public function getName(): string;
public function getSize(): int;
}
class File implements FileSystemItem {
public function __construct(
private string $name,
private int $size
) {}
public function getName(): string {
return $this->name;
}
public function getSize(): int {
return $this->size;
}
}
class Folder implements FileSystemItem {
private array $items = [];
public function __construct(private string $name) {}
public function getName(): string {
return $this->name;
}
public function add(FileSystemItem $item): void {
$this->items[] = $item;
}
public function getSize(): int {
$total = 0;
foreach ($this->items as $item) {
$total += $item->getSize();
}
return $total;
}
}
Bây giờ bạn có thể xây dựng cấu trúc cây và xử lý chúng một cách thống nhất:
<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));
$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));
$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));
echo $root->getSize();
Đầu ra:
1810Đoạn code phía client gọi getSize() mà không cần quan tâm nó đang xử lý một tệp đơn lẻ hay một cấu trúc thư mục lồng nhau sâu. Mẫu này tự xử lý đệ quy (recursion), giúp bạn dễ dàng làm việc với các hệ phân cấp phức tạp.
Thử thách
DễHãy xây dựng một hệ thống sơ đồ tổ chức bằng Mẫu tổng hợp (Composite Pattern). Các công ty có cấu trúc phân cấp, trong đó phòng ban có thể chứa nhân viên và các phòng ban nhỏ hơn: đây là cấu trúc cây hoàn hảo để bạn tính tổng đồng nhất trên toàn tổ chức.
Bạn sẽ sắp xếp code trong bốn tệp:
OrganizationUnit.php: Định nghĩa giao diệnOrganizationUnitmà cả nhân viên riêng lẻ lẫn phòng ban đều triển khai. Giao diện này cần có hai phương thức:getName(): stringvàgetSalary(): int. Giao diện chung này cho phép bạn xử lý nhân viên riêng lẻ và toàn bộ phòng ban theo cùng một cách khi tính chi phí.Employee.php: Đưa giao diện OrganizationUnit vào và tạo lớpEmployeetriển khai giao diện đó. Nhân viên là nút lá trong cây tổ chức. Họ có tên và mức lương nhưng không thể chứa các đơn vị khác. Dùng cú pháp rút gọn hàm khởi tạo để nhận tên và mức lương, rồi triển khai cả hai phương thức của giao diện.Department.php: Đưa giao diện OrganizationUnit vào và tạo lớpDepartmenttriển khai giao diện đó. Phòng ban là một đối tượng tổng hợp có thể chứa cả nhân viên lẫn các phòng ban khác. Phòng ban của bạn cần:- Nhận tên trong hàm khởi tạo
- Triển khai
getName()để trả về tên phòng ban - Có phương thức
add(OrganizationUnit $unit): voidđể thêm nhân viên hoặc phòng ban nhỏ hơn - Triển khai
getSalary()để trả về tổng mức lương của tất cả đơn vị mà phòng ban chứa, bằng cách cộng đệ quy mọi mức lương bên trong
main.php: Đưa các tệp Employee và Department vào. Bạn sẽ nhận một đầu vào: một chuỗi JSON biểu diễn cấu trúc tổ chức.JSON có định dạng sau:
{"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}Mỗi khóa là tên một phòng ban, còn giá trị của nó là một mảng nhân viên cùng tên và mức lương của họ.
Tạo một
Departmentgốc có tên"Company". Với mỗi phòng ban trong JSON, hãy tạo mộtDepartment, thêm nhân viên vào đó, rồi thêm phòng ban ấy vào Company. Cuối cùng, in ra tổng mức lương của công ty theo định dạng sau:Total Salary: [amount]
Điểm hay của Mẫu tổng hợp là khi gọi getSalary() trên phòng ban Company, phương thức này tự động tính tổng mức lương của tất cả phòng ban và nhân viên lồng bên trong. Code phía gọi không cần biết cấu trúc nội bộ.
Tự làm thử
<?php
require_once 'Employee.php';
require_once 'Department.php';
// Đọc đầu vào JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);
// TODO: Tạo Department gốc có tên "Company"
// TODO: Lặp qua từng phòng ban trong dữ liệu JSON
// Với mỗi phòng ban:
// - Tạo một đối tượng Department mới
// - Thêm từng nhân viên vào phòng ban đó
// - Thêm phòng ban vào Company
// TODO: In tổng tiền lương theo định dạng: Total Salary: [amount]
?>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ặp13Mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổ hợpMẫu Repository2Khô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