Mẫu thiết kế Iterator thuộc nhóm hành vi (behavioral pattern), ra đời nhằm giải quyết bài toán duyệt các phần tử trong cấu trúc dữ liệu mà không làm lộ chi tiết nội bộ của chúng. Thay vì để người dùng trực tiếp thao tác với mảng hoặc danh sách, mẫu Iterator cung cấp một lớp trung gian – bộ lặp – giúp tách biệt logic duyệt khỏi cấu trúc lưu trữ.
Giả sử có hai phòng ban trong công ty: phòng ban Minh và phòng ban Huy, mỗi phòng sử dụng kiểu dữ liệu khác nhau để lưu thông tin nhân viên. Phòng Minh dùng ArrayList, còn phòng Huy lại dùng mảng tĩnh. Mục tiêu là cho phép quản lý duyệt toàn bộ nhân viên mà không cần quan tâm đến cách lưu trữ bên trong từng phòng.
Định nghĩa lớp thực thể Employee
Trước tiên, ta xây dựng lớp đại diện cho nhân viên:
public class Employee {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "name=" + name + " age=" + age;
}
}
Tạo giao diện Iterator và Company
Để thống nhất cách duyệt, ta định nghĩa giao diện Iterator:
public interface Iterator {
boolean hasNext();
Object next();
}
Và giao diện Company – đại diện cho một đơn vị có thể cung cấp bộ lặp:
public interface Company {
Iterator iterator();
}
Cài đặt cụ thể cho từng phòng ban
Phòng ban Minh sử dụng List để lưu dữ liệu:
public class CompanyMin implements Company {
private List<Employee> staffList = new ArrayList<>();
public CompanyMin() {
staffList.add(new Employee("xiaoA", 33));
staffList.add(new Employee("xiaoQ", 34));
staffList.add(new Employee("xiaoF", 35));
staffList.add(new Employee("xiaoG", 36));
staffList.add(new Employee("xiaoH", 37));
}
@Override
public Iterator iterator() {
return new MinDepartmentIterator(staffList);
}
}
Bộ lặp dành cho phòng Minh:
public class MinDepartmentIterator implements Iterator {
private List<Employee> employees;
private int currentIndex = 0;
public MinDepartmentIterator(List<Employee> employees) {
this.employees = employees;
}
@Override
public boolean hasNext() {
return currentIndex < employees.size() && employees.get(currentIndex) != null;
}
@Override
public Object next() {
if (hasNext()) {
return employees.get(currentIndex++);
}
return null;
}
}
Phòng ban Huy dùng mảng cố định:
public class CompanyHui implements Company {
private Employee[] team = new Employee[3];
public CompanyHui() {
team[0] = new Employee("tom", 23);
team[1] = new Employee("jim", 24);
team[2] = new Employee("tim", 25);
}
@Override
public Iterator iterator() {
return new HuiTeamIterator(team);
}
}
Bộ lặp tương ứng:
public class HuiTeamIterator implements Iterator {
private Employee[] members;
private int index = 0;
public HuiTeamIterator(Employee[] members) {
this.members = members;
}
@Override
public boolean hasNext() {
return index < members.length && members[index] != null;
}
@Override
public Object next() {
if (hasNext()) {
return members[index++];
}
return null;
}
}
Chạy thử nghiệm
Viết lớp kiểm thử để duyệt tất cả nhân viên từ các phòng ban khác nhau:
public class IterationDemo {
public static void main(String[] args) {
displayEmployees(new CompanyMin().iterator());
displayEmployees(new CompanyHui().iterator());
}
private static void displayEmployees(Iterator iter) {
while (iter.hasNext()) {
System.out.println(iter.next());
}
}
}
Kết quả đầu ra:
name=xiaoA age=33 name=xiaoQ age=34 name=xiaoF age=35 name=xiaoG age=36 name=xiaoH age=37 name=tom age=23 name=jim age=24 name=tim age=25
Thông qua việc áp dụng Iterator Pattern, ta đã tách biệt hoàn toàn logic duyệt khỏi cấu trúc dữ liệu. Dù mỗi phòng dùng kiểu lưu trữ khác nhau, nhưng phía client chỉ cần gọi iterator() và dùng vòng lặp chung mà không cần biết chi tiết triển khai bên trong. Điều này tăng tính mở rộng và bảo trì cho hệ thống.
Trong thực tế, Java Collections Framework đã tích hợp sẵn cơ chế này thông qua java.util.Iterator, do đó lập trình viên hiếm khi phải tự viết lại trừ khi xây dựng cấu trúc dữ liệu tùy chỉnh.