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 địnhMẫ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.
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.DocumentGeneratorcần có:Một phương thức
finaltê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 raReport 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 raCustomer: [name]trên một dòng, sau đó inAmount 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 raThank 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
ReportGeneratorvới tiêu đề rồi gọigenerateDocument(). In một dòng trống để ngăn cách. Sau đó, tạo mộtInvoiceGeneratorvới tên khách hàng và số tiền rồi gọigenerateDocument().
Đầ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()
}
}
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
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online