آموزش iterator در جاوا را با ما بیاموزید

آموزش iterator در جاوا
تاریخ بروزرسانی : 20 مرداد 1399 | تعداد بازدید : 3286 | زمان خواندن مقاله : 15 دقیقه
جاوا،

در دنیای شی گرایی کمتر برنامه نویس حرفه ای وجود دارد که با مفهومی به نام دیزاین پترن یا الگوهای طراحی آشنا نباشد.
الگوهای طراحی برای اولین بار توسط یک گروه 4 نفری به نام GOF معرفی شد که 23 الگوی طراحی شده را مطرح کردند و به طور کلی در 3 دسته الگوهای ایجادی، ساختاری و رفتاری قرار دادند.

الگوهای ساختاری یا Structural، با روابط بین موجودیت ها سروکار دارند و کار موجودیت ها با یکدیگر را ساده تر می کنند.الگوهای ایجادی یا Creational، ساخت آبجکت ها را ساده تر می کنند. یعنی به جای نمونه سازی مستقیم از آبجکت ها، برای شما یک آبجکت می سازد و در تصمیم گیری، شما را درباره انتخاب اینکه کدام یک از آبجکت ها برای یک مورد خاص ایجاد شود را آسان می کند. و اما الگوهای رفتاری یا Behavioral، برای مدیریت الگوریتم ها، روابط و مسئولیت بین آبجکت ها استفاده می شود. در ادامه ما می خواهیم شما را با الگوی طراحی iterator آشنا کنیم.

آموزش جاوا

الگوی طراحی یا دیزاین پترن چیست؟

قبل از معرفی iterator، می خواهیم کمی درباره مفهوم خود دیزاین پترن صحبت کنیم. دیزاین پترن ها، راه کارهای بهینه با قابلیت استفاده مجدد برای مشکلات برنامه نویسان هستند که متعلق به زبان خاصی نبوده و اگر در محل اشتباهی از آنها استفاده کنیم منجر به فاجعه و ایجاد مشکلات بسیار خواهند شد. این الگوها در حوزه ی object و class محقق می شوند. الگوهای حوزه ای class در زمان کامپایل محقق می شوند و از ارث بری استفاده می کنند اما الگوهای حوزه ای object در زمان اجرا و با ساخت objectهای لازم، محقق می شوند.

بیشتر بخوانید: برای یادگیری انواع مختلف دیزاین پترن در جاوا این مقاله را حتما بخوانwink


تعریف iterator:

این دیزاین پترن یک الگوی رفتاری و به معنی تکرار کردن است و ما به ساختاری نیاز داریم که 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 دارد که  شماره فعلی کتاب را داخل مجموعه نگه میدارد و البته تابع 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;
    }
}

بیشتر مطالعه کنید: Abstraction در جاوا را در 10 دقیقه بیاموزwink

 

خب میریم سراغ تعریف 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
حالا که متوجه شدید چه اتفاقی در این الگو رخ می دهد، شاید بشود با توجه به انتظاراتمان در کد، با کمی تغییر و استفاده از ساختارهای دیگر مثل استفاده از لیست پیوندی یا غیره، پیاده سازیِ خود رو از این الگو داشته باشیم! این را هم بدانید که بعضی کلاس های  جاوا مثل کلاس ArrayList  تابعی دارند به اسم iterator که میتوانیم بدون اینکه خودمان بخواهیم الگوی iterator را پیاده سازی و کلاس هایش را تعریف کنیم، از آن به راحتی استفاده کنیم.

جمع بندی:

خب دوستان عزیز درسمنی، شما را با مفهوم الگوهای طراحی و دیزاین پترن iterator آشنا کردیم و با مثالی به آموزش iterator در جاوا پرداختیم. به عنوان جمع بندی این مطلب می خواهیم متدهای iterator را به شما معرفی کنیم که عبارت اند از:

  • ( ) boolean hasNext: اگر عناصر بیشتری وجود داشته باشد true در غیر اینصورت false بر می گرداند.
  • ( ) Object next: عنصر بعدی را بازمی گرداند اگر یک عنصر بعدی وجود نداشته باشد NoSuchElementException  را برمی گرداند.
  • ( ) void remove: عنصر فعلی را حذف می کند. 

اگر علاقه مند به شروع برنامه نویسی با جاوا هستید می توانید از آموزش جاوا که به صورت رایگان در سایت برای شما قرار دارد، استفاده کنید و نظرات و پیشنهادات خودتان را درباره دوره با ما به اشتراک بگذارید.
 


کلمات کلیدی :
جاوا