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.
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átIterator<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.Playlistcầ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ứcaddSong(String song)để thêm bài hát vào danh sách phát.Playlistcần có phương thứccreateIterator()trả về mộtIterator<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ênPlaylistIterator, 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ớpSongđơ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ứcgetTitle()và phương thứctoString()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
Playlistcó 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ượngSong(đối tượng này sẽ hiển thị dưới dạngPlaying: [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
}
}
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