Menu
CoddyTech

Mẫu phương thức khuôn mẫu

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

Mẫu phương thức là một mẫu thiết kế hành vi, xác định khung sườn của thuật toán trong lớp cơ sở, cho phép các lớp con ghi đè những bước cụ thể mà không làm thay đổi cấu trúc tổng thể. Lớp cơ sở điều khiển quy trình, còn các lớp con tùy chỉnh từng phần riêng lẻ.

Hãy nghĩ đến việc tạo nhiều loại tài liệu khác nhau.

Mọi tài liệu đều tuân theo cùng một quy trình: tạo phần đầu, thêm nội dung, tạo phần chân. Nhưng chi tiết của mỗi bước sẽ khác nhau tùy theo loại tài liệu. Phương thức mẫu xác định trình tự này, còn các lớp con triển khai từng chi tiết.

<?php
abstract class DocumentGenerator {
    // Phương thức khuôn mẫu (template method) xác định cấu trúc thuật toán (algorithm)
    public final function generate(): string {
        $output = $this->createHeader();
        $output .= $this->createContent();
        $output .= $this->createFooter();
        return $output;
    }
    
    // Các bước mà lớp con (subclass) cần triển khai
    abstract protected function createHeader(): string;
    abstract protected function createContent(): string;
    abstract protected function createFooter(): string;
}

class HtmlDocument extends DocumentGenerator {
    protected function createHeader(): string {
        return "<html><head></head><body>";
    }
    
    protected function createContent(): string {
        return "<p>Hello World</p>";
    }
    
    protected function createFooter(): string {
        return "</body></html>";
    }
}

class TextDocument extends DocumentGenerator {
    protected function createHeader(): string {
        return "=== DOCUMENT ===\n";
    }
    
    protected function createContent(): string {
        return "Hello World\n";
    }
    
    protected function createFooter(): string {
        return "=== END ===";
    }
}

$html = new HtmlDocument();
echo $html->generate() . "\n";

$text = new TextDocument();
echo $text->generate();

Đầu ra:

<html><head></head><body><p>Hello World</p></body></html>
=== DOCUMENT ===
Hello World
=== END ===

Hãy chú ý đến từ khóa final trong phương thức mẫu. Từ khóa này ngăn các lớp con thay đổi cấu trúc của thuật toán. Mẫu này phù hợp khi bạn có nhiều lớp tuân theo cùng một quy trình nhưng khác nhau về chi tiết triển khai. Để thêm một loại tài liệu mới, bạn chỉ cần tạo một lớp con mà không cần sửa code hiện có.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống tạo báo cáo bằng Mẫu Template Method. Các loại báo cáo khác nhau có cùng cấu trúc, gồm phần tiêu đề, phần dữ liệu và phần tóm tắt, nhưng mỗi loại định dạng các phần này theo cách riêng. Phương thức mẫu sẽ xác định quy trình này, còn các lớp con tùy chỉnh từng bước.

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

  • ReportGenerator.php: Tạo lớp trừu tượng ReportGenerator để định nghĩa khung sườn của thuật toán tạo báo cáo. Lớp của bạn cần:
    • Nhận tiêu đề báo cáo trong hàm khởi tạo (dùng tính năng khai báo thuộc tính trong hàm khởi tạo)
    • Có phương thức công khai final generate(): string, gọi lần lượt ba bước: createTitle(), createBody() và createSummary(), rồi nối các kết quả lại
    • Khai báo ba phương thức trừu tượng có phạm vi protected mà các lớp con phải triển khai: createTitle(): string, createBody(): string và createSummary(): string
  • HtmlReport.php: Nạp tệp ReportGenerator và tạo lớp HtmlReport kế thừa lớp đó. Báo cáo này định dạng đầu ra dưới dạng HTML:
    • createTitle() trả về "<h1>[title]</h1>"
    • createBody() trả về "<p>Report data goes here</p>"
    • createSummary() trả về "<footer>End of report</footer>"
  • PlainTextReport.php: Nạp tệp ReportGenerator và tạo lớp PlainTextReport kế thừa lớp đó. Báo cáo này dùng định dạng văn bản đơn giản:
    • createTitle() trả về "### [title] ###\n"
    • createBody() trả về "Report data goes here\n"
    • createSummary() trả về "--- End of report ---"
  • main.php: Nạp cả hai tệp báo cáo. Bạn sẽ nhận hai đầu vào: loại báo cáo ("html" hoặc "text") và tiêu đề báo cáo.

    Dựa trên loại báo cáo, hãy tạo bộ tạo báo cáo phù hợp với tiêu đề đã cho. Gọi generate() và in toàn bộ báo cáo.

Mẫu Template Method đảm bảo mọi báo cáo đều tuân theo cùng cấu trúc ba bước, đồng thời cho phép mỗi loại báo cáo định dạng riêng từng phần. Từ khóa final trong generate() ngăn các lớp con thay đổi trình tự của thuật toán. Chúng chỉ có thể tùy chỉnh từng bước riêng lẻ.

Tự làm thử

<?php

require_once 'HtmlReport.php';
require_once 'PlainTextReport.php';

// Đọc đầu vào
$reportType = trim(fgets(STDIN));
$reportTitle = trim(fgets(STDIN));

// TODO: Dựa vào loại báo cáo ("html" hoặc "text"),
// tạo trình tạo báo cáo phù hợp với tiêu đề đã cho

// TODO: Gọi generate() và in ra báo cáo đầy đủ

?>
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