Mẫu trang trí
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 77 trên 91.
Mẫu Decorator là một mẫu thiết kế cấu trúc, cho phép bạn thêm hành vi mới vào đối tượng một cách linh hoạt bằng cách bọc chúng trong các đối tượng bao bọc đặc biệt gọi là decorator. Khác với kế thừa, vốn thêm hành vi tại thời điểm biên dịch, decorator thêm hành vi khi chạy chương trình.
Hãy tưởng tượng một quán cà phê. Bạn bắt đầu với một ly cà phê cơ bản, sau đó thêm các món như sữa, đường hoặc kem tươi.
Mỗi phần bổ sung bọc quanh cà phê ban đầu và thêm mô tả cùng chi phí cho cà phê. Điểm mấu chốt là cả cà phê cơ bản lẫn các lớp trang trí đều dùng chung một giao diện.
<?php
interface Coffee {
public function getDescription(): string;
public function getCost(): float;
}
class SimpleCoffee implements Coffee {
public function getDescription(): string {
return "Simple Coffee";
}
public function getCost(): float {
return 2.00;
}
}
Decorator triển khai cùng một giao diện và bọc một đối tượng Coffee khác:
<?php
class MilkDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Milk";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.50;
}
}
class SugarDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Sugar";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.25;
}
}
$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);
echo $coffee->getDescription() . " = $" . $coffee->getCost();
Đầu ra:
Simple Coffee, Milk, Sugar = $2.75Mỗi decorator bọc đối tượng trước đó, tạo thành một chuỗi. Mẫu này cho phép bạn kết hợp các hành vi theo bất kỳ thứ tự và số lượng nào mà không cần tạo một lớp cho mọi tổ hợp có thể. Muốn thêm hai phần sữa? Chỉ cần bọc hai lần.
Thử thách
DễHãy xây dựng hệ thống đặt pizza bằng mẫu Decorator (Decorator Pattern). Bạn sẽ bắt đầu với một chiếc pizza cơ bản rồi thêm các món phủ một cách linh hoạt: mỗi món phủ bọc chiếc pizza và bổ sung vào phần mô tả cũng như giá tiền, giống như ví dụ về cà phê trong bài học.
Bạn sẽ sắp xếp code trong bốn tệp:
Pizza.php: Định nghĩa giao diệnPizzađể đặt ra quy ước cho tất cả pizza và món phủ. Giao diện này cần có hai phương thức:getDescription(): stringvàgetPrice(): float.BasePizza.php: Nạp giao diện Pizza và tạo lớpMargheritaPizzatriển khai giao diện đó. Đây là pizza cơ bản, có phần mô tả là"Margherita"và giá là8.00.Toppings.php: Nạp giao diện Pizza và tạo ba lớp trang trí triển khai giao diện đó. Mỗi lớp trang trí bọc một đối tượngPizza(nhận đối tượng đó trong hàm khởi tạo) và bổ sung vào cả phần mô tả lẫn giá tiền:CheeseTopping: Thêm", Extra Cheese"vào phần mô tả và1.50vào giá tiềnPepperoniTopping: Thêm", Pepperoni"vào phần mô tả và2.00vào giá tiềnMushroomTopping: Thêm", Mushrooms"vào phần mô tả và1.25vào giá tiền
main.php: Nạp các tệp BasePizza và Toppings. Bạn sẽ nhận một đầu vào: danh sách món phủ được phân tách bằng dấu phẩy để thêm vào, ví dụ"cheese,pepperoni"hoặc"mushroom,cheese,cheese".Bắt đầu với một
MargheritaPizza. Với mỗi món phủ trong đầu vào, hãy bọc pizza bằng lớp trang trí phù hợp:"cheese"được bọc bằngCheeseTopping"pepperoni"được bọc bằngPepperoniTopping"mushroom"được bọc bằngMushroomTopping
Sau khi thêm tất cả món phủ, hãy in phần mô tả và giá của pizza hoàn chỉnh theo định dạng này:
[description] = $[price]Định dạng giá tiền với hai chữ số thập phân.
Lưu ý rằng bạn có thể thêm cùng một món phủ nhiều lần: gọi món thêm phô mai kép chỉ đơn giản là bọc bằng CheeseTopping hai lần. Đó chính là sức mạnh linh hoạt của mẫu Decorator!
Tự làm thử
<?php
require_once 'BasePizza.php';
require_once 'Toppings.php';
// Đọc đầu vào: danh sách topping được phân tách bằng dấu phẩy
$input = trim(fgets(STDIN));
$toppings = explode(',', $input);
// Bắt đầu với MargheritaPizza cơ bản
$pizza = new MargheritaPizza();
// TODO: Duyệt qua từng topping và bọc pizza bằng decorator phù hợp
// - "cheese" -> bọc bằng CheeseTopping
// - "pepperoni" -> bọc bằng PepperoniTopping
// - "mushroom" -> bọc bằng MushroomTopping
// TODO: In kết quả theo định dạng: [description] = $[price]
// Định dạng giá với 2 chữ số thập phân bằng number_format()
?>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