Menu
CoddyTech

Mẫu Iterator

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 76 trên 87.

Mẫu iterator là một mẫu thiết kế hành vi, cung cấp cách truy cập lần lượt các phần tử trong một tập hợp mà không để lộ cấu trúc bên trong. Dù bạn làm việc với mảng, danh sách liên kết hay cây, iterator vẫn cho bạn một cách thống nhất để duyệt qua các phần tử.

Mẫu này tách logic duyệt khỏi chính tập hợp. Mẫu này gồm hai thành phần chính: giao diện Iterator định nghĩa các phương thức để duyệt qua các phần tử và một Iterable (hoặc Aggregate) tạo các iterator cho tập hợp của nó:

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

Bộ lặp duy trì trạng thái duyệt riêng, cho phép nhiều bộ lặp duyệt cùng một tập hợp một cách độc lập. Các bên sử dụng bộ lặp mà không cần biết tập hợp lưu trữ dữ liệu như thế nào:

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Các giao diện Iterable và Iterator tích hợp sẵn trong Java tuân theo chính xác mẫu này, vì vậy bạn có thể dùng vòng lặp for nâng cao với bất kỳ lớp nào triển khai Iterable. Mẫu Iterator rất cần thiết khi bạn cần cung cấp nhiều phương thức duyệt hoặc ẩn cấu trúc nội bộ phức tạp khỏi code phía client.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống danh sách phát bằng mẫu Iterator! Bạn sẽ tạo một danh sách phát nhạc để lưu các bài hát và cung cấp một trình lặp tùy chỉnh để duyệt lần lượt qua chúng mà không để lộ cách lưu trữ bài hát bên trong. Đây là trường hợp sử dụng hoàn hảo cho mẫu Iterator, giúp người dùng duyệt bộ sưu tập thông qua một giao diện gọn gàng, thống nhất.

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

  • Iterator.java: Định nghĩa giao diện tổng quát Iterator<T> với hai phương thức: hasNext() trả về một giá trị boolean cho biết còn phần tử nào hay không, và next() trả về phần tử tiếp theo thuộc kiểu T.
  • Playlist.java: Tạo lớp tập hợp chứa các bài hát. Playlist cần lưu bài hát trong một mảng String có dung lượng cố định (truyền vào hàm khởi tạo) và theo dõi số bài hát đã được thêm. Thêm phương thức addSong(String song) để thêm bài hát vào danh sách phát.

    Playlist cần có phương thức createIterator() trả về một Iterator<String>. Hãy triển khai phương thức này bằng cách tạo một lớp bên trong riêng tư có tên PlaylistIterator, lớp này triển khai giao diện Iterator. Trình lặp bên trong này tự quản lý vị trí chỉ số và duyệt qua mảng bài hát, trả về lần lượt từng bài hát.

  • Song.java: Tạo một lớp Song đơn giản để bao bọc tiêu đề bài hát. Lớp này cần có hàm khởi tạo nhận tiêu đề (String), phương thức getTitle() và phương thức toString() trả về Playing: [title].
  • Main.java: Kết hợp hệ thống trình lặp! Bạn sẽ nhận một đầu vào: danh sách tiêu đề bài hát được phân tách bằng dấu phẩy (ví dụ: Bohemian Rhapsody,Stairway to Heaven,Hotel California).

    Tạo một Playlist có sức chứa 10 bài hát. Phân tích đầu vào và thêm từng tiêu đề bài hát vào danh sách phát. Sau đó, lấy một trình lặp từ danh sách phát và dùng nó để duyệt qua tất cả bài hát, in từng bài hát sau khi bọc trong một đối tượng Song (đối tượng này sẽ hiển thị dưới dạng Playing: [title]).

    Sau khi duyệt qua tất cả bài hát, in Playlist complete! trên một dòng mới.

Bạn sẽ nhận một đầu vào: chuỗi tiêu đề bài hát được phân tách bằng dấu phẩy.

Ví dụ, với đầu vào Yesterday,Imagine,Let It Be, đầu ra sẽ là:

Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!

Hãy lưu ý cách lớp Main dùng các phương thức hasNext() và next() của trình lặp để duyệt danh sách phát mà không cần biết gì về cấu trúc mảng bên dưới. Trình lặp đóng gói toàn bộ logic duyệt, giữ kín cách biểu diễn nội bộ của tập hợp đối với code phía sử dụng!

Tự làm thử

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Tạo Playlist có sức chứa 10 bài hát
        
        // TODO: Phân tích đầu vào (các mục được phân tách bằng dấu phẩy) và thêm từng tên bài hát vào playlist
        
        // TODO: Lấy bộ lặp từ playlist bằng createIterator()
        
        // TODO: Dùng bộ lặp để duyệt qua tất cả bài hát
        // Với mỗi bài hát, tạo đối tượng (object) Song chứa bài hát đó rồi in ra
        
        // TODO: In "Playlist complete!" sau khi duyệt qua tất cả bài hát
    }
}
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