Menu
CoddyTech

Mẫu Iterator

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 56 trên 61.

Mẫu thiết kế Iterator pattern cung cấp cách truy cập tuần tự các phần tử trong một tập hợp mà không để lộ cấu trúc bên trong. Thay vì cho người dùng truy cập trực tiếp vào một mảng, bạn cung cấp cho họ một đối tượng biết cách duyệt qua dữ liệu từng phần tử một.

Bộ lặp thường cần hai thông tin: tham chiếu đến tập hợp và vị trí hiện tại. Trong C, ta tạo một struct để lưu trạng thái này:

typedef struct {
    int* data;      // con trỏ đến mảng
    int size;       // tổng số phần tử
    int current;    // vị trí hiện tại
} IntIterator;

Trình lặp cung cấp hai hàm chính. has_next() kiểm tra xem còn phần tử nào để đọc không, còn next() trả về phần tử hiện tại và chuyển vị trí tiến lên:

int has_next(IntIterator* it) {
    return it->current < it->size;
}

int next(IntIterator* it) {
    return it->data[it->current++];
}

Dùng trình lặp giúp code gọn gàng và ẩn đi các chi tiết về mảng:

IntIterator it = create_iterator(numbers, 5);
while (has_next(&it)) {
    printf("%d\n", next(&it));
}

Bên gọi không cần biết numbers là một mảng hay cách truy cập bằng chỉ số hoạt động. Sự trừu tượng hóa này giúp bạn dễ dàng thay đổi cấu trúc dữ liệu bên dưới sau này, chẳng hạn từ mảng sang danh sách liên kết, mà không cần sửa code sử dụng trình lặp.

challenge icon

Thử thách

Dễ

Hãy tạo một bộ lặp NumberList: một lớp trừu tượng gọn gàng, cho phép bạn duyệt qua một tập hợp số nguyên mà không để lộ cấu trúc mảng bên dưới.

Bạn sẽ chia code thành ba tệp:

  • iterator.h: Định nghĩa một struct IntIterator chứa con trỏ đến mảng số nguyên, tổng số phần tử trong tập hợp và vị trí hiện tại. Khai báo ba hàm: create_iterator (nhận con trỏ mảng và kích thước, trả về một bộ lặp đã khởi tạo), has_next (kiểm tra xem còn phần tử nào không) và next (trả về phần tử hiện tại rồi tăng vị trí). Đừng quên các include guard!
  • iterator.c: Cài đặt các hàm của bộ lặp. Hàm create_iterator sẽ trả về một IntIterator theo giá trị, với vị trí hiện tại được đặt thành 0. Hàm has_next trả về 1 nếu còn phần tử để đọc, nếu không thì trả về 0. Hàm next trả về phần tử ở vị trí hiện tại, sau đó tăng vị trí.
  • main.c: Đọc số lượng phần tử, sau đó đọc từng giá trị số nguyên vào một mảng. Tạo bộ lặp cho mảng này, rồi dùng vòng lặp while cùng với has_next và next để duyệt và in từng phần tử trên một dòng riêng.

Chương trình sẽ nhận:

  1. Số lượng phần tử trong mảng
  2. Từng giá trị số nguyên trên một dòng riêng

Dùng mẫu thiết kế bộ lặp để in tất cả phần tử: không truy cập trực tiếp vào chỉ số mảng trong vòng lặp duyệt!

Đầu ra ví dụ khi đầu vào lần lượt là 4, rồi 10, 20, 30, 40:

10
20
30
40

Đầu ra ví dụ khi đầu vào lần lượt là 3, rồi -5, 0, 100:

-5
0
100

Đầu ra ví dụ khi đầu vào lần lượt là 1, rồi 42:

42

Bộ lặp che giấu cách lưu trữ dữ liệu. Vòng lặp chính chỉ cần hỏi “còn phần tử nào không?” và “đưa phần tử tiếp theo cho mình”, mà không cần biết gì về chỉ số mảng hay cách bố trí bộ nhớ.

Tự làm thử

#include <stdio.h>
#include "iterator.h"

int main() {
    int n;
    scanf("%d", &n);
    
    int arr[n];
    
    // TODO: Đọc n giá trị số nguyên (integer) vào mảng (array)
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    
    // TODO: Tạo bộ lặp cho mảng
    
    // TODO: Dùng vòng lặp while với has_next và next để duyệt mảng
    // và in mỗi phần tử (element) trên một dòng riêng
    // KHÔNG dùng chỉ số (index) trực tiếp để truy cập mảng trong vòng lặp duyệt!
    
    return 0;
}
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 C online