Menu
CoddyTech

Mẫu Template Method

Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 73 trên 87.

Mẫu Template Method là một mẫu thiết kế hành vi, định nghĩa khung của một 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 thay đổi cấu trúc tổng thể. Hãy hình dung mẫu này như một công thức nấu ăn: quy trình nấu chung vẫn giữ nguyên, nhưng từng nguyên liệu hoặc kỹ thuật có thể khác nhau.

Mẫu này sử dụng một lớp trừu tượng với phương thức khuôn mẫu, gọi một chuỗi các bước. Một số bước có phần triển khai mặc định, còn các bước khác là trừu tượng và các lớp con phải triển khai:

abstract class DataProcessor {
    // Phương thức mẫu (template method) xác định cấu trúc thuật toán
    public final void process() {
        readData();
        processData();
        saveData();
    }
    
    abstract void readData();
    abstract void processData();
    
    // Phần triển khai mặc định (phương thức móc, hook method)
    void saveData() {
        System.out.println("Saving to default location");
    }
}

class CSVProcessor extends DataProcessor {
    void readData() {
        System.out.println("Reading CSV file");
    }
    
    void processData() {
        System.out.println("Parsing CSV data");
    }
}

class XMLProcessor extends DataProcessor {
    void readData() {
        System.out.println("Reading XML file");
    }
    
    void processData() {
        System.out.println("Parsing XML data");
    }
}

Phương thức mẫu thường được đánh dấu là final để ngăn các lớp con thay đổi cấu trúc của thuật toán. Khi được gọi, phương thức này thực hiện cùng một trình tự cho tất cả các lớp con:

DataProcessor csv = new CSVProcessor();
csv.process();
// Đầu ra:
// Đang đọc tệp CSV
// Đang phân tích dữ liệu CSV
// Đang lưu vào vị trí mặc định

Mẫu Template Method phù hợp khi bạn có các thuật toán cùng cấu trúc nhưng khác nhau ở một số bước cụ thể. Mẫu này thúc đẩy việc tái sử dụng code bằng cách đặt logic chung trong lớp cơ sở, đồng thời cho phép tùy chỉnh thông qua tính kế thừa.

challenge icon

Thử thách

Dễ

Hãy cùng xây dựng một hệ thống tạo tài liệu bằng mẫu phương thức khuôn mẫu (Template Method Pattern)! Bạn sẽ tạo một framework để các loại tài liệu khác nhau (báo cáo và hóa đơn) tuân theo cùng một quy trình tạo tài liệu nhưng tùy chỉnh một số bước cụ thể. Mẫu này rất phù hợp với những tình huống mà quy trình tổng thể đã cố định, nhưng chi tiết thay đổi tùy theo loại tài liệu.

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

  • DocumentGenerator.java: Tạo lớp cơ sở trừu tượng định nghĩa phương thức khuôn mẫu. DocumentGenerator cần có:

    Một phương thức final tên là generateDocument(), định nghĩa khung thuật toán bằng cách gọi lần lượt ba bước: createHeader(), createBody() và createFooter().

    Hai phương thức trừu tượng: createHeader() và createBody(), các lớp con phải triển khai.

    Một phương thức móc createFooter() có phần triển khai mặc định để in ra --- End of Document ---.

  • ReportGenerator.java: Tạo một lớp con cụ thể để tạo báo cáo. Hàm khởi tạo của lớp này nhận tiêu đề báo cáo (String).

    Phương thức createHeader() in ra === REPORT: [title] ===

    Phương thức createBody() in ra Report content goes here...

    Dùng phần chân trang mặc định được kế thừa.

  • InvoiceGenerator.java: Tạo một lớp con cụ thể khác để tạo hóa đơn. Hàm khởi tạo của lớp này nhận tên khách hàng (String) và số tiền (double).

    Phương thức createHeader() in ra *** INVOICE ***

    Phương thức createBody() in ra Customer: [name] trên một dòng, sau đó in Amount Due: $[amount] trên dòng tiếp theo (định dạng số tiền với hai chữ số thập phân).

    Ghi đè createFooter() để in ra Thank you for your business!

  • Main.java: Kết hợp hệ thống phương thức khuôn mẫu của bạn! Bạn sẽ nhận ba đầu vào: tiêu đề báo cáo (String), tên khách hàng (String) và số tiền hóa đơn (double).

    Tạo một ReportGenerator với tiêu đề rồi gọi generateDocument(). In một dòng trống để ngăn cách. Sau đó, tạo một InvoiceGenerator với tên khách hàng và số tiền rồi gọi generateDocument().

Đầu vào gồm ba giá trị theo thứ tự: tiêu đề báo cáo (String), tên khách hàng (String) và số tiền hóa đơn (double).

Ví dụ, với đầu vào Q4 Sales, Acme Corp và 1500.00, đầu ra sẽ là:

=== REPORT: Q4 Sales ===
Report content goes here...
--- End of Document ---

*** INVOICE ***
Customer: Acme Corp
Amount Due: $1500.00
Thank you for your business!

Lưu ý rằng cả hai loại tài liệu đều tuân theo cùng quy trình ba bước được định nghĩa trong phương thức khuôn mẫu, nhưng mỗi loại tùy chỉnh các bước theo cách khác nhau. ReportGenerator dùng phần chân trang mặc định, còn InvoiceGenerator cung cấp phần chân trang riêng. Đó là sức mạnh của phương thức móc trong mẫu phương thức khuôn mẫu!

Tự làm thử

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Đọc dữ liệu đầu vào
        String reportTitle = scanner.nextLine();
        String customerName = scanner.nextLine();
        double invoiceAmount = scanner.nextDouble();
        
        // TODO: Tạo ReportGenerator với tiêu đề và gọi generateDocument()
        
        // TODO: In ra một dòng trống để phân tách
        
        // TODO: Tạo InvoiceGenerator với tên khách hàng và số tiền, rồi gọi generateDocument()
        
    }
}
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 Java online