Lặp đa hình
Thuộc phần Lập trình hướng đối tượng trong hành trình C của Coddy. Bài học 42 trên 61.
Bạn đã học cách tạo giao diện và triển khai chúng bằng nhiều hàm cụ thể khác nhau. Lợi ích thực sự xuất hiện khi bạn có thể xử lý đồng nhất một tập hợp các đối tượng khác nhau: duyệt qua chúng và gọi cùng một con trỏ hàm trên từng đối tượng, bất kể hành vi cụ thể nào được gán cho con trỏ đó.
Hãy tưởng tượng bạn có một mảng các cấu trúc Action, mỗi cấu trúc được gán một hàm execute riêng:
typedef void (*ExecuteFunc)(void);
typedef struct {
const char* name;
ExecuteFunc execute;
} Action;
void jump(void) { printf("Jumping!\n"); }
void run(void) { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }Bạn có thể tạo một mảng chứa các hành động này, mỗi hành động được liên kết với một hàm khác nhau, rồi duyệt qua chúng bằng một vòng lặp for đơn giản:
int main() {
Action actions[3] = {
{ "Jump", jump },
{ "Run", run },
{ "Rest", rest }
};
for (int i = 0; i < 3; i++) {
printf("%s: ", actions[i].name);
actions[i].execute();
}
return 0;
}Vòng lặp không biết cũng không quan tâm mỗi hành động thực hiện những gì bên trong. Nó chỉ gọi execute() trên từng phần tử.
Đây là tính đa hình đang hoạt động: cùng một đoạn code xử lý các đối tượng có hành vi hoàn toàn khác nhau. Việc thêm một kiểu hành động mới không đòi hỏi thay đổi logic của lần lặp; bạn chỉ cần thêm một phần tử khác vào mảng.
Thử thách
DễHãy xây dựng một hệ thống quản lý Task minh họa cách lặp đa hình: xử lý một tập hợp các đối tượng có chung giao diện nhưng thực hiện các hành vi khác nhau.
Bạn sẽ sắp xếp code trong ba tệp:
task.h: Định nghĩa giao diện tác vụ tại đây. Tạo một kiểu con trỏ hàm có tênTaskFunc, không nhận tham số và không trả về gì. Sau đó, định nghĩa một structTaskcóname(kiểuconst char*) và con trỏ hàmrunkiểuTaskFunc.task.c: Viết ba hàm tác vụ khác nhau, mỗi hàm đại diện cho một loại công việc:backup_task: in raBacking up data...cleanup_task: in raCleaning up files...report_task: in raGenerating report...
main.c: Kết hợp mọi thứ tại đây. Tạo một mảng các structTask, mỗi struct được gán một hàm tác vụ khác nhau. Sau đó, lặp qua mảng và thực thi từng tác vụ theo cách đa hình. Với mỗi tác vụ, in tên của tác vụ rồi gọi hàmrun.
Chương trình nhận một đầu vào duy nhất: số tác vụ cần chạy (1, 2 hoặc 3).
Tạo một mảng có đúng ba tác vụ theo thứ tự sau: tác vụ sao lưu có tên Backup, tác vụ dọn dẹp có tên Cleanup và tác vụ tạo báo cáo có tên Report. Dựa trên số lượng đầu vào, chỉ lặp qua đúng số tác vụ đó, bắt đầu từ đầu mảng.
Với mỗi tác vụ trong vòng lặp, in tên tác vụ theo sau là dấu hai chấm và khoảng trắng, rồi gọi hàm run.
Đầu ra ví dụ khi đầu vào là 2:
Backup: Backing up data...
Cleanup: Cleaning up files...Đầu ra ví dụ khi đầu vào là 3:
Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...Điểm mấu chốt là vòng lặp không cần biết từng tác vụ thực hiện việc gì. Vòng lặp chỉ cần in tên và gọi run() trên từng phần tử. Cùng một đoạn code lặp có thể xử lý đồng nhất mọi loại tác vụ. Nhớ dùng include guard trong tệp tiêu đề.
Tự làm thử
#include <stdio.h>
#include "task.h"
// Khai báo các hàm (function) tác vụ được triển khai trong task.c
void backup_task(void);
void cleanup_task(void);
void report_task(void);
int main() {
int count;
scanf("%d", &count);
// TODO: Tạo một mảng (array) gồm 3 struct Task theo thứ tự sau:
// 1. Tác vụ sao lưu có tên "Backup", dùng backup_task
// 2. Tác vụ dọn dẹp có tên "Cleanup", dùng cleanup_task
// 3. Tác vụ báo cáo có tên "Report", dùng report_task
// TODO: Duyệt qua 'count' tác vụ trong mảng
// Với mỗi tác vụ, in ra "<name>: " rồi gọi hàm run của tác vụ đó
return 0;
}
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 mô-đun
Tệp tiêu đềInclude guardTệp mã nguồnHàm staticÔn tập: Máy tính mô-đun4Tính đóng gói
Khái niệm con trỏ mờĐịnh nghĩa struct mờHàm truy xuất và hàm gánKiểm tra tính hợp lệ trong hàm gánÔn tập: Hộp bí mật2Đối tượng và phương thức
Struct dưới dạng đối tượngCon trỏ 'Self'Tính đúng đắn của constCon trỏ và giá trịPhương thức hỗ trợÔn tập: trình quản lý Point5Dự án: Tài khoản ngân hàng đơn giản
Thiết lập dự ánTriển khai tài khoản3Vòng đời đối tượng
Mẫu hàm khởi tạoMẫu hàm hủyKhởi tạo trên ngăn xếpSao chép sâuÔn tập: lớp bao bọc chuỗi6Kế thừa bằng cách kết hợp
Nhúng structQuy tắc thành viên đầu tiênTruy cập thành viên lớp chaUpcastingÔn tập: hệ phân cấp hình dạngTự luyện tập: Trình biên dịch C online