میتونین برای آشنایی با الگوهای طراحی (یا همون دیزاین پترن های) زبان جاوا به مقاله ی یک فنجان جاوا - دیزاین پترن ها Design Patterns مراجعه کنین.
(همونطور که گفته شده این الگو زیرمجموعه ی الگوهای رفتاری (Behavioral) هست)
واژه Iterate به معنی تکرار کردن هست. توی iteration ما نیاز به ساختاری داریم که object فعلی رو نگهداریم و در عین حال بدونیم object بعدی یا قبلی (و نه کل مجموعه) چیه. در حقیقت Iterator زمانی استفاده میشه که میخوایم توی چرخه (یا روند رفت یا برگشتی) به اجزای یک مجموعه دسترسی داشته باشیم.
به عبارت دیگه iterator الگوییه که به ما اجازه دسترسی و پیمایش یه تجمع از اشیاء رو میده بدون نیاز به افشا کردن کل مجموعه.
خب با یه مثال ببینیم منظورمون چیه:
اول یه اینترفیس به اسم Iterator تعریف میکنیم
interface Iterator{ public boolean hasNext(); public Object next(); }
توابع hasNext و next اجزای لازمی هستن که توی پیاده سازی این پترن همیشه ازشون استفاده میکنیم (و طبیعتا مشخصه که hasNext به ما میگه عنصر بعدی ای وجود داره یا نه و تابع next باید عنصر بعدی رو برگردونه).
خب توی قدم بعدی یه اینترفیس دیگه به اسم Container تعریف میکنیم که کارش ساخت یه شي از Iterator هست
interface Container{ public Iterator createIterator(); }
خب حالا نگاهی به کلاس زیر بندازیم
class BooksCollection implements Container{ private String m_titles[] = {"test0","test1","test2","test3","test4"}; public Iterator createIterator(){ BookIterator result = new BookIterator(); return result; } private class BookIterator implements Iterator{ private int m_position; public boolean hasNext(){ if (m_position < m_titles.length) return true; else return false; } public Object next(){ if (this.hasNext()) return m_titles[m_position++]; else return null; } } }
اول ببینیم کلاس BookIterator چیه. خب ما میخوایم مجموعه از کتاب ها داشته باشم و همونطور که گفتیم این الگو نیاز به یه Iterator داره. پس کلاس BookIterator رو تعریف کردیم که یه عضو داره به اسم m_position که داره ایندکس فعلی کتابمون رو داخل مجموعه نگه میداره٬ و البته Iteratorمون رو implement و طبیعتا توابع hasNext (میبینه بعد از m_position عضوی وجود داره یا نه) و next (اگه عنصری بعد از m_position باشه٬ m_position یدونه زیاد میشه و عضو بعدی رو برمیگردونیم) رو پیاده سازی کرده.
و این کلاس خودش داخل کلاسی به اسم BooksCollection هست که آرایه ی m_titles رو بصورت پیشفرض با چند مقدار داره٬ و تابع createIterator رو هم بواسطه ی implement کردن کلاس Container پیاده سازی کرده و یه BookIterator برمیگردونه.
و در نهایت برای تست کد زیر رو اجرا میکنیم:
Iterator iterator = new BooksCollection().createIterator(); while (name.hasNext() ){ System.out.println(name.next()); }
و خروجی این کد بصورت زیره:
test0 test1 test2 test3 test4
میتونیم کد تستمون رو بصورت زیر هم بنویسیم که همون خروجی بالارو نتیجه میده:
BooksCollection booksRepository = new BooksCollection(); for(Iterator iter = booksRepository.createIterator(); iter.hasNext();) System.out.println(iter.next());
و ما به همین راحتی یه مثال ساده از الگوی Iterator رو پیاده سازی کردیم.
میشه به این الگو توابع remove یا insertAfter یا insertBefore رو هم اضافه کرد. خود جاوا کلاسی داره به اسم Iterator که خوبه نگاهی بهش بندازین و ما هم توی مثال بعدی از این کلاس استفاده خواهیم کرد.
خب با یه مثال دیگه ادامه میدیم. کلاس شکل رو مینویسیم:
public class Shape { private int id; private String name; public Shape(int id, String name){ this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
و یه کلاس دیگه هم تعریف میکنیم برای اضافه کردن و نگهداری شکلها:
public class ShapeStorage { private Shape []shapes = new Shape[5]; private int index; public void addShape(String name){ int i = index++; shapes[i] = new Shape(i,name); } public Shape[] getShapes(){ return shapes; } }
خب میریم سراغ تعریف یه Iterator برای مجموعه ی شکلهامون:
public class ShapeIterator implements Iterator<Shape>{ private Shape [] shapes; int pos; public ShapeIterator(Shape []shapes){ this.shapes = shapes; } @Override public boolean hasNext() { if(pos >= shapes.length || shapes[pos] == null) return false; return true; } @Override public Shape next() { return shapes[pos++]; } @Override public void remove() { if(pos <=0 ) throw new IllegalStateException("Illegal position"); if(shapes[pos-1] !=null){ for (int i= pos-1; i<(shapes.length-1);i++){ shapes[i] = shapes[i+1]; } shapes[shapes.length-1] = null; } } }
همونطور که میبینیم این کلاس یه آرایه از Shapeها داره و Iterator رو implement کرده (توابع hasNext و next و remove پیاده سازی شده). مشخصه که تابع hasNext به ما میگه شکل بعدی ای وجود داره یا نه٬ تابع next در صورت وجود شکل بعدی رو برمیگردونه و تابع remove اگه توی pos (یا همون position) فعلی توی آرایمون شکلی وجود داشته باشه اونو حذف میکنه!
و برای تست کد زیر رو اجرا میکنیم:
ShapeStorage storage = new ShapeStorage(); storage.addShape("Polygon"); storage.addShape("Hexagon"); storage.addShape("Circle"); storage.addShape("Rectangle"); storage.addShape("Square"); ShapeIterator iterator = new ShapeIterator(storage.getShapes()); while(iterator.hasNext()){ System.out.println(iterator.next()); }
اتفاقی که اینجا میفته به این صورته که اول میایم یه ShapeStorage تعریف میکنیم که داخلش یه آرایه از shapeها رو نگه میداره. و به کمک تابع addShape بهش شکل های Polygon و Hexagon و Circle و Rectangle و Square رو اضافه میکنیم (با توجه به نوع پیاده سازی تابع addShape فقط کافیه اسم شکلمون رو بدیم و اون خودش اتوماتیک id هر شکل که اضافه میشه رو یکی یکی افزایش میده).
توی قدم بعدی یه شی از ShapeIterator ساختیم و آرایه ی شکلهای موجود توی storage رو بهش دادیم.
و در آخر هم رفتیم سراغ نمایش یا به اصطلاح iterate کردن آرایه ی شکل هامون.
و همونطور که انتظار داریم خروجی بصورت زیر هست:
ID: 0 Shape: Polygon ID: 1 Shape: Hexagon ID: 2 Shape: Circle ID: 3 Shape: Rectangle ID: 4 Shape: Square
خب فکر کنم متوجه شدیم که چه اتفاقی افتاد. برای اینکه تابع remove رو هم تست کرده باشیم قطعه کد زیر رو اجرا میکنیم:
ShapeIterator removeTestIterator = new ShapeIterator(storage.getShapes()); while(removeTestIterator.hasNext()){ System.out.println(removeTestIterator.next()); removeTestIterator.remove(); }
خب شما بگین انتظار داریم توی خروجی چی ببینیم
.
.
.
.
.
کاری که اینجا اتفاق میفته اینه که اولین بار با صدا زدن
removeTestIterator.next();
و با رسیدن به عبارت
return shapes[pos++];
اول کد زیر اجرا میشه
return shapes[pos];
و بعدش با اجرای دستور زیر یدونه به pos اضافه میشه
pos++
پس ما توی خروجی عنصر اول رو میبینیم (که Polygon هست) و pos داره به عنصر دوم (Hexagon) اشاره میکنه. حالا با رسیدن به دستور زیر
removeTestIterator.remove();
جایی که pos داره بهش اشاره میکنه (یعنی Hexagon) حذف میشه. و این روند همینطور ادامه پیدا میکنه و در حقیقت ما داریم یکی در میون شکلهای با pos زوج (۰ و ۲ و ۴) رو نشون میدیم و شکلهای با pos فرد (۱ و ۳) رو حذف میکنیم! بنابراین خروجی به صورت زیر خواهد بود:
ID: 0 Shape: Polygon ID: 2 Shape: Circle ID: 4 Shape: Square
حالا که متوجه شدیم چه اتفاقی توی این الگو میفته، شاید بشه با توجه به انتظاراتمون توی کد، با یذره تغییر و استفاده از ساختارهای دیگه ای مثل استفاده از لیست پیوندی یا غیره، پیاده سازیِ خودمون رو از این الگو داشته باشیم!
خب فکر کنم به خوبی با مفهوم Iterate کردن و الگوی Iterator آشنا شدیم. به عنوان آخرین مثال هم بدونیم که بعضی کلاسای جاوا مثل کلاس ArrayList خودش تابعی داره به اسم iterator که میتونیم بدون اینکه خودمون بخوایم الگوی iterator رو پیاده سازی و کلاس هاش رو تعریف کنیم٬ ازش براحتی استفاده کنیم. مثالش هم بصورت زیر خواهد بود:
ArrayList<String> al = new ArrayList<String>(); al.add("M"); al.add("I"); al.add("S"); al.add("A"); al.add("G"); al.add("H"); System.out.print("Original list: "); Iterator<String> itr = al.iterator(); while (itr.hasNext()) { String element = itr.next(); System.out.print(element + " "); } System.out.println(); System.out.print("Modified contents: "); ListIterator<String> litr = al.listIterator(); while (litr.hasNext()) { String element = litr.next(); String newElement = "*" + element + "+"; litr.set(newElement); System.out.print(newElement + " "); } System.out.println(); System.out.print("Modified list backwards: "); while (litr.hasPrevious()) { String element = litr.previous(); System.out.print(element + " "); }
و خروجی به این صورته:
Original list: M I S A G H Modified contents: *M+ *I+ *S+ *A+ *G+ *H+ Modified list backwards: *H+ *G+ *A+ *S+ *I+ *M+
منتشر شده در ویرگول توسط محمد قدسیان https://virgool.io/@mohammad.ghodsian
https://virgool.io/@mohammad.ghodsian/java-iterator-design-pattern-d8x2pyrv3vdi