author-pic

Ferry S

An ISTJ, Type 5, Engineer, Gamer, and Thriller-Movies-Lover
Contoh Iterator Design Pattern
Tue. May 23rd, 2023 02:20 PM5 mins read
Contoh Iterator Design Pattern
Source: Bing Image Creator - Iteration Flow

Design pattern ini cukup populer digunakan. Salah satu contoh class di Java yang menggunakan Iterator Design Pattern adalah Iterator. Iterator tersebut tugasnya untuk membuat berbagai collection elemennya bisa di-iterasi satu-persatu. Jadi sebuah collection kita bungkus di suatu objek yang nantinya bisa kita modifikasi atau lintasi lewat objek tersebut melalui method yang disediakan pada objek. Kita bisa menambahkan behavior-behavior tertentu pada saat modifikasi atau melintasi collection. Jadi ketika kita ingin melakukan modifikasi pada collection tersebut bisa dilakukan lewat objek tanpa harus bersentuhan langsung dengan collectionnya.

Iterator Design Pattern adalah Behavioral Design Pattern yang membuat kita bisa melintasi elemen-elemen dari sebuah collection lewat objek tanpa harus mengeksploitasi data strukturnya (seperti List, Tree, atau lainnya) ke luar.

Design Pattern

Kita akan membuat logic sederhana untuk menyimpan dan memainkan playlist lagu. Di dalamnya terdapat fitur play, insert, delete, sorting, dll.

Interface Media

public interface Media{
	String getName();

	Duration getDuration();
}

Class Song

public class Song implements Media{
	private final String name;
	private final Duration duration;

	public Song(String name, Duration duration){
		this.name = name;
		this.duration = duration;
	}

	public String getName(){
		return name;
	}

	public Duration getDuration(){
		return duration;
	}

	@Override
	public String toString(){
		return "Song{" +
				"name='" + name + '\'' +
				", duration=" + (duration.getSeconds() / 60) + " minutes and " + (duration.getSeconds() % 60) + " seconds" +
				'}';
	}

}

Contoh penggunaan

public static void main(String[] args){
	List<Media> songs = new ArrayList<>();
	Collections.addAll(songs,
			new Song("bintang di surga", Duration.ofMinutes(3).plusSeconds(25)),
			new Song("roman picisan", Duration.ofMinutes(4).plusSeconds(25)),
			new Song("bila kau tak di sampingku", Duration.ofMinutes(3).plusSeconds(10)),
			new Song("cinta dan benci", Duration.ofMinutes(4).plusSeconds(49)),
			new Song("love story", Duration.ofMinutes(5).plusSeconds(22)),
			new Song("scar tissue", Duration.ofMinutes(3).plusSeconds(35)),
			new Song("anthem part 2", Duration.ofMinutes(3).plusSeconds(45))
	);
	Iterator<Media> songIterator = songs.iterator();
	System.out.println(songIterator.next());
	System.out.println(songIterator.next());
	songs.removeIf(s -> s.getName().equals("love story"));
	songs.sort(Comparator.comparing(Media::getDuration));
	System.out.println(songs.stream().map(Media::getName).collect(Collectors.joining(", ")));
	System.out.println(songs.get(3));
	System.out.println(songs.get(ThreadLocalRandom.current().nextInt(0, songs.size())));
}

Pada code di atas kita menampilkan dan modifikasi playlist lewat collection langsung.

Permasalahan dari code di atas adalah sulit untuk memaintain code karena misalkan kita ingin menambahkan fitur-fitur baru seperti shuffle playlist, random sorting, dan lainnya maka kita akan mengeksploitasi logic-nya ke luar. Untuk itu kita perlu bungkus collectionnya ke sebuah objek dan logic-nya dihandle di dalam objek tersebut.

Sekarang kita bakal bikin use case di atas pakai Iterator Design Pattern😎.

Interface MediaPlayer

public interface MediaPlayer<M extends Media>{
	void insert(M... medias);

	void removeByName(String mediaName);

	void printAllNames();

	void sortByDuration();

	void sortByName();

	void sortByRandom();

	void play();

	void next();

	void previous();

	void gotoNumber(int number);

	void shuffleOrderPlayer();

	void naturalOrderPlayer();
}

Class SongPlayer

public class SongPlayer implements MediaPlayer<Song>{
	private static final String SHUFFLE = "shuffle";
	private static final String NATURAL = "natural";
	private final List<Song> songs = new ArrayList<>();
	private int lastPlay;
	private String order = NATURAL;

	@Override
	public void insert(Song... medias){
		Collections.addAll(this.songs, medias);
	}

	@Override
	public void removeByName(String mediaName){
		this.songs.removeIf(s -> s.getName().equals(mediaName));
	}

	@Override
	public void printAllNames(){
		System.out.println();
		System.out.println("songs = " + this.songs.stream().map(Song::getName).collect(Collectors.joining(", ")));
		System.out.println();
	}

	@Override
	public void sortByDuration(){
		songs.sort(Comparator.comparing(Song::getDuration));
	}

	@Override
	public void sortByName(){
		songs.sort(Comparator.comparing(Song::getName));
	}

	@Override
	public void sortByRandom(){
		int size = this.songs.size();
		for(int i = 0; i < size; i++){
			int nexted = ThreadLocalRandom.current().nextInt(0, size);
			Song song = this.songs.get(i);
			this.songs.set(i, this.songs.get(nexted));
			this.songs.set(nexted, song);
		}
	}

	@Override
	public void play(){
		System.out.println("no. " + (this.lastPlay + 1) + ": " + this.songs.get(this.lastPlay));
	}

	@Override
	public void next(){
		if(this.order.equals(SHUFFLE)){
			int randomIndex = ThreadLocalRandom.current().nextInt(0, this.songs.size());
			while(randomIndex == this.lastPlay){
				randomIndex = ThreadLocalRandom.current().nextInt(0, this.songs.size());
			}
			this.lastPlay = randomIndex;
		} else {
			if(this.lastPlay + 1 >= this.songs.size()){
				System.out.println("no next song");
				return;
			}
			this.lastPlay++;
		}
		play();
	}

	@Override
	public void previous(){
		if(--this.lastPlay < 0){
			this.lastPlay = 0;
		}
		play();
	}

	@Override
	public void gotoNumber(int number){
		if(number <= 0 || number > this.songs.size()){
			throw new IllegalArgumentException("can't find song number " + number);
		}
		this.lastPlay = number - 1;
		play();
	}

	@Override
	public void shuffleOrderPlayer(){
		this.order = SHUFFLE;
	}

	@Override
	public void naturalOrderPlayer(){
		this.order = NATURAL;
	}

}
public static void main(String[] args){
	MediaPlayer<Song> player = new SongPlayer();
	player.insert(
			new Song("bintang di surga", Duration.ofMinutes(3).plusSeconds(25)),
			new Song("roman picisan", Duration.ofMinutes(4).plusSeconds(25)),
			new Song("bila kau tak di sampingku", Duration.ofMinutes(3).plusSeconds(10)),
			new Song("cinta dan benci", Duration.ofMinutes(4).plusSeconds(49)),
			new Song("love story", Duration.ofMinutes(5).plusSeconds(22)),
			new Song("scar tissue", Duration.ofMinutes(3).plusSeconds(35)),
			new Song("anthem part 2", Duration.ofMinutes(3).plusSeconds(45))
	);
	player.printAllNames();
	player.play();
	player.next();
	player.next();
	player.gotoNumber(7);
	player.previous();
	player.sortByRandom();
	player.printAllNames();
	player.gotoNumber(1);
	player.sortByDuration();
	player.printAllNames();
	player.gotoNumber(1);
	player.sortByName();
	player.printAllNames();
	player.gotoNumber(1);
	player.shuffleOrderPlayer();
	player.next();
	player.next();
	player.next();
	player.removeByName("love story");
	player.printAllNames();
}

Kita perlu membuat interface MediaPlayer agar interchangable, jadi nantinya ga hanya playlist lagu, kita juga bisa membuat MediaPlayer untuk playlist video. Kita juga membuat implementasinya, dalam hal ini SongPlayer untuk memutar playlist lagu. Untuk class Song dan interface Media tidak perlu perubahan apa-apa. Collectionnya kita enkapsulasi di dalam object. Kita bisa menambahkan beberapa fitur yang lebih advanced pada method objeknya seperti next, previous, sort by random, shuffle order, natural order, sort by name, sort by duration, dan berbagai fitur lainnya. Untuk penggunaannya simple, tinggal eksekusi method dari MediaPlayer saja sesuai kebutuhan.

Dengan Itertaor kita bisa membuat logic yang simple digunakan dan mudah di-maintain. Kita hanya perlu menyediakan objek dan methodnya saja untuk dipakai client code. Jadi kita tinggal gunakan method dari objek tersebut untuk menampilkan atau memodifikasi playlist. Untuk penambahan atau perubahan fitur tinggal maintain di satu class aja.

Iterator Design Pattern cukup populer digunakan seperti pada class Iterator pada Java. Kita hanya perlu membungkus collection ke dalam objek dan menyediakan method untuk menampilkan atau memodifikasi collection. Kita tidak perlu repot-repot memodifikasi langsung ke collectionnya, hanya perlu gunakan method yang disediakan pada object yang dibuat. Kita bisa menambahkan logic tertentu sesuai kebutuhan pada saat menampilkan atau memodifikasi collection.