עיצוב האיטרטור הוא אחד מתבניות ההתנהגותיות. תבנית האיטרטור משמשת לספק דרך סטנדרטית לנוע בין קבוצה של אובייקטים. תבנית האיטרטור נמצאת בשימוש נרחב ב-מסגרת האוסף של Java. ממשק האיטרטור מספק שיטות לניווט דרך אוסף.
תבנית עיצוב האיטרטור
לפי הגרמה לאובייקטים, מטרת תבנית עיצוב האיטרטור היא:
לספק דרך לגשת לאלמנטים של אובייקט אגרגט בלתי חשוף את הייצוג התחתון שלו.
תבנית האיטרטור אינה רק על ניווט דרך אוסף, אפשר לספק איטרטורים שונים בהתאם לדרישות שלנו. עיצוב האיטרטור מסתיר את היישום האמיתי של הניווט דרך האוסף ותוכניות לקוח פשוט משתמשות בשיטות האיטרטור.
דוגמה לתבנית עיצוב איטרטור
שנתן לנו להבין את דפוס האיטרטור בעזרת דוגמה פשוטה. נניח שיש לנו רשימת ערוצי רדיו והתוכנית הלקוח רוצה לעבור דרכם אחד-אחד או בהתבסס על סוג הערוץ. לדוגמה, ישנם תוכניות לקוח שרק מעוניינות בערוצים באנגלית ורוצות לעבד אותם בלבד, אין להן רצון לעבד ערוצים מסוגים אחרים. לכן, אנו יכולים לספק ללקוח אוסף של ערוצים ולאפשר לו לכתוב את הלוגיקה לעבור דרך הערוצים ולהחליט האם לעבד אותם. אך יש לשים לב שפתרון זה מתקשה לאור כמה בעיות, כמו שהלקוח צריך ליצור את הלוגיקה לעברת הניווט. אי אפשר לוודא שהלוגיקה של הלקוח נכונה. ומיותר לציין שאם מספר הלקוחות יגדל, יהיה קשה לאחזר את התחזוקה. במקום זאת, אנו יכולים להשתמש בדפוס האיטרטור ולספק איטרציה בהתבסס על סוג הערוץ. יש לוודא שהתוכנית של הלקוח יכולה לגשת לרשימת הערוצים רק דרך האיטרטור. החלק הראשון של היישום הוא להגדיר את החוזה עבור האוסף וממשקי האיטרטור. ChannelTypeEnum.java
package com.journaldev.design.iterator;
public enum ChannelTypeEnum {
ENGLISH, HINDI, FRENCH, ALL;
}
ChannelTypeEnum הוא enum ב-Java המגדיר את כל סוגי הערוצים השונים. Channel.java
package com.journaldev.design.iterator;
public class Channel {
private double frequency;
private ChannelTypeEnum TYPE;
public Channel(double freq, ChannelTypeEnum type){
this.frequency=freq;
this.TYPE=type;
}
public double getFrequency() {
return frequency;
}
public ChannelTypeEnum getTYPE() {
return TYPE;
}
@Override
public String toString(){
return "Frequency="+this.frequency+", Type="+this.TYPE;
}
}
Channel הוא קלאס POJO פשוט הכולל מאפיינים של תדר וסוג ערוץ. ChannelCollection.java
package com.journaldev.design.iterator;
public interface ChannelCollection {
public void addChannel(Channel c);
public void removeChannel(Channel c);
public ChannelIterator iterator(ChannelTypeEnum type);
}
ממשק ChannelCollection מגדיר את החוזה עבור המימוש של מחלקת האוסף שלנו. שים לב שישנם שיטות להוספה והסרה של ערוצים, אך לא קיימת שיטה שמחזירה את רשימת הערוצים. ל-ChannelCollection יש שיטה שמחזירה את האיטרטור לצורך ניווט. ממשק ChannelIterator מגדיר את השיטות הבאות; ChannelIterator.java
package com.journaldev.design.iterator;
public interface ChannelIterator {
public boolean hasNext();
public Channel next();
}
עכשיו הממשק הבסיסי והמחלקות היסודיות שלנו מוכנים, בואו נמשיך למימוש של מחלקת האוסף והאיטרטור. ChannelCollectionImpl.java
package com.journaldev.design.iterator;
import java.util.ArrayList;
import java.util.List;
public class ChannelCollectionImpl implements ChannelCollection {
private List<Channel> channelsList;
public ChannelCollectionImpl() {
channelsList = new ArrayList<>();
}
public void addChannel(Channel c) {
this.channelsList.add(c);
}
public void removeChannel(Channel c) {
this.channelsList.remove(c);
}
@Override
public ChannelIterator iterator(ChannelTypeEnum type) {
return new ChannelIteratorImpl(type, this.channelsList);
}
private class ChannelIteratorImpl implements ChannelIterator {
private ChannelTypeEnum type;
private List<Channel> channels;
private int position;
public ChannelIteratorImpl(ChannelTypeEnum ty,
List<Channel> channelsList) {
this.type = ty;
this.channels = channelsList;
}
@Override
public boolean hasNext() {
while (position < channels.size()) {
Channel c = channels.get(position);
if (c.getTYPE().equals(type) || type.equals(ChannelTypeEnum.ALL)) {
return true;
} else
position++;
}
return false;
}
@Override
public Channel next() {
Channel c = channels.get(position);
position++;
return c;
}
}
}
שים לב למימוש של מחלקה פנימית של ממשק האיטרטור כך שהמימוש לא יוכל להיות בשימוש של אוסף אחר. גם מחלקות האוסף עוקבות אחרי גישה זו, וכולן יש להן מימוש של מחלקה פנימית של ממשק האיטרטור. בואו נכתוב תוכנית פשוטה לבדיקת תבנית האיטרטור לשימוש באוסף שלנו ובאיטרטור לניווט באמצעות אוסף של ערוצים. IteratorPatternTest.java
package com.journaldev.design.iterator;
public class IteratorPatternTest {
public static void main(String[] args) {
ChannelCollection channels = populateChannels();
ChannelIterator baseIterator = channels.iterator(ChannelTypeEnum.ALL);
while (baseIterator.hasNext()) {
Channel c = baseIterator.next();
System.out.println(c.toString());
}
System.out.println("******");
// איטרטור לסוג הערוץ
ChannelIterator englishIterator = channels.iterator(ChannelTypeEnum.ENGLISH);
while (englishIterator.hasNext()) {
Channel c = englishIterator.next();
System.out.println(c.toString());
}
}
private static ChannelCollection populateChannels() {
ChannelCollection channels = new ChannelCollectionImpl();
channels.addChannel(new Channel(98.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(99.5, ChannelTypeEnum.HINDI));
channels.addChannel(new Channel(100.5, ChannelTypeEnum.FRENCH));
channels.addChannel(new Channel(101.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(102.5, ChannelTypeEnum.HINDI));
channels.addChannel(new Channel(103.5, ChannelTypeEnum.FRENCH));
channels.addChannel(new Channel(104.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(105.5, ChannelTypeEnum.HINDI));
channels.addChannel(new Channel(106.5, ChannelTypeEnum.FRENCH));
return channels;
}
}
כאשר אני מריץ את התוכנית למעלה, היא יוצרת את הפלט הבא;
Frequency=98.5, Type=ENGLISH
Frequency=99.5, Type=HINDI
Frequency=100.5, Type=FRENCH
Frequency=101.5, Type=ENGLISH
Frequency=102.5, Type=HINDI
Frequency=103.5, Type=FRENCH
Frequency=104.5, Type=ENGLISH
Frequency=105.5, Type=HINDI
Frequency=106.5, Type=FRENCH
******
Frequency=98.5, Type=ENGLISH
Frequency=101.5, Type=ENGLISH
Frequency=104.5, Type=ENGLISH
נקודות חשובות בעיצוב תבנית האיטרטור
- תבנית האיטרטור שימושית כאשר ברצונך לספק דרך סטנדרטית לעבור על אוסף ולהסתיר את הלוגיקה של המימוש מתוכנית הלקוח.
- לוגיקת החזרה כתובה באופן מוטבע באוסף עצמו והיא עוזרת לתוכנית הלקוח לעבור עליהם בקלות.
דוגמה לתבנית עיצוב Iterator ב-JDK
כולנו יודעים שה-Iterator בספריית האוסף הוא הדוגמה הטובה ביותר למימוש של תבנית Iterator. אבל האם את/ה יודע/ת שגם קבוצת הקוד java.util.Scanner מממשת את ממשק ה-Iterator? קרא/י את הפוסט הזה כדי ללמוד עוד על קבוצת הקוד Java Scanner. זו כל המידע שיש על תבנית עיצוב ה-Iterator, אני מקווה/ת שזה מועיל וקל להבנה.
Source:
https://www.digitalocean.com/community/tutorials/iterator-design-pattern-java