Mẫu Factory
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 72 trên 91.
Factory Pattern là một mẫu thiết kế khởi tạo, giao việc tạo đối tượng cho một lớp hoặc phương thức riêng. Thay vì dùng trực tiếp new trong toàn bộ code, bạn nhờ một factory tạo đối tượng giúp mình.
Mẫu này đặc biệt hữu ích khi bạn có nhiều lớp liên quan dùng chung một giao diện và muốn quyết định sẽ khởi tạo lớp nào dựa trên một điều kiện. Hãy xem xét một hệ thống thông báo có thể gửi tin nhắn qua các kênh khác nhau:
<?php
interface Notification {
public function send(string $message): string;
}
class EmailNotification implements Notification {
public function send(string $message): string {
return "Email: $message";
}
}
class SmsNotification implements Notification {
public function send(string $message): string {
return "SMS: $message";
}
}
Nếu không có factory, bạn sẽ phải rải các câu lệnh if và lệnh gọi new khắp nơi. Factory tập trung logic này:
<?php
class NotificationFactory {
public static function create(string $type): Notification {
return match($type) {
'email' => new EmailNotification(),
'sms' => new SmsNotification(),
default => throw new InvalidArgumentException("Unknown type: $type")
};
}
}
$notification = NotificationFactory::create('email');
echo $notification->send('Hello!');
Đầu ra:
Email: Hello!Lợi ích chính là code của bạn phụ thuộc vào giao diện Notification, chứ không phụ thuộc vào các lớp cụ thể. Nếu sau này bạn thêm lớp PushNotification, bạn chỉ cần cập nhật factory. Mọi code sử dụng factory sẽ tự động có thể dùng kiểu mới mà không cần chỉnh sửa.
Thử thách
DễHãy xây dựng hệ thống tạo tài liệu bằng Factory Pattern. Các loại tài liệu khác nhau (PDF, HTML, văn bản thuần) dùng chung một giao diện nhưng tạo ra đầu ra khác nhau: đây là tình huống lý tưởng để tập trung việc tạo đối tượng vào một factory.
Bạn sẽ sắp xếp code trong bốn tệp:
Document.php: Tạo giao diệnDocumentđể xác định quy ước mà mọi loại tài liệu phải tuân theo. Giao diện này cần có một phương thức duy nhấtrender(string $content): string, nhận nội dung và trả về đầu ra tài liệu đã định dạng.Documents.php: Bao gồm giao diện Document và tạo ba lớp triển khai giao diện này:PdfDocument: Phương thứcrender()trả về"PDF: [content]"HtmlDocument: Phương thứcrender()trả về"<html>[content]</html>"TextDocument: Phương thứcrender()trả về"TXT: [content]"
DocumentFactory.php: Bao gồm tệp Documents và tạo lớpDocumentFactoryvới phương thức tĩnhcreate(string $type): Document. Phương thức này cần dùngmatchđể trả về đối tượng tài liệu phù hợp dựa trên loại tài liệu:"pdf"trả về mộtPdfDocumentmới"html"trả về mộtHtmlDocumentmới"text"trả về mộtTextDocumentmới- Với mọi loại khác, hãy ném ngoại lệ
InvalidArgumentExceptionkèm thông báo"Unknown document type: [type]"
main.php: Bao gồm tệp DocumentFactory. Bạn sẽ nhận được hai đầu vào: loại tài liệu và nội dung cần kết xuất.Dùng factory để tạo loại tài liệu phù hợp, sau đó gọi phương thức
render()của tài liệu với nội dung và in kết quả.Trên dòng mới, in
"Factory benefit: Adding new types only requires updating the factory"để làm nổi bật lý do mẫu thiết kế này hữu ích.
Factory Pattern giúp code linh hoạt. Khi cần thêm MarkdownDocument sau này, bạn chỉ cần cập nhật factory, còn toàn bộ code hiện có vẫn tiếp tục hoạt động như cũ.
Tự làm thử
<?php
require_once 'DocumentFactory.php';
// Đọc đầu vào
$type = trim(fgets(STDIN));
$content = trim(fgets(STDIN));
// TODO: Dùng DocumentFactory để tạo loại tài liệu phù hợp
// TODO: Gọi phương thức (method) render() với nội dung và in ra kết quả
// TODO: In "Factory benefit: Adding new types only requires updating the factory" trên một dòng mới
?>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ánTự luyện tập: Trình biên dịch PHP online