JavaRush /وبلاگ جاوا /Random-FA /الگوی تکرار کننده

الگوی تکرار کننده

در گروه منتشر شد
امروز در مورد اینکه Iterator در جاوا چیست و چرا به آن نیاز است صحبت خواهیم کرد .
الگوی تکرار کننده - 1
همانطور که احتمالا قبلاً می دانید، جاوا دارای یک رابط مجموعه فوق العاده است که رابط Iterator را پیاده سازی می کند. اجازه دهید فوراً رزرو کنم: رابط تکرار کننده نباید با الگوی تکرار کننده در جاوا اشتباه گرفته شود! و برای روشن شدن، اجازه دهید ابتدا به رابط کاربری نگاه کنیم.
به معنای واقعی کلمه، "Iterator" را می توان به عنوان "نیروی بی رحم " ترجمه کرد . یعنی موجودیت خاصی است که می تواند از طریق تمام عناصر مجموعه تکرار کند. علاوه بر این، به شما امکان می دهد این کار را بدون پرداختن به ساختار داخلی و ترتیب مجموعه ها انجام دهید.
بیایید برای لحظه ای تصور کنیم که هیچ تکرار کننده ای در جاوا وجود ندارد. در این صورت، هرکسی باید در اعماق مجموعه ها غوطه ور شود و واقعاً بفهمد که چه چیزی ArrayListبا LinkedListو HashSetاز چه چیزی متفاوت است TreeSet.

روش هایی که Iterator باید اجرا کند

boolean hasNext()- اگر هنوز مقادیری در شی تکرار شونده (در حال حاضر یک مجموعه) باقی مانده باشد، trueاگر مقادیر بیشتری وجود نداشته باشد، متد برمی گردد false. E next()- عنصر بعدی مجموعه (شیء) را برمی گرداند. اگر هیچ عنصر دیگری وجود نداشته باشد (چک وجود نداشت و وقتی به انتهای مجموعه رسیدیم hasNext()تماس گرفتیم )، متد پرتاب می شود . - عنصری را که آخرین بار توسط . روش می تواند پرتاب کند: next()NoSuchElementExceptionvoid remove()next()
  • UnsupportedOperationException، اگر این تکرار کننده از روش پشتیبانی نمی کند remove()(مثلاً در مورد مجموعه های فقط خواندنی)
  • IllegalStateException، اگر متد next()هنوز فراخوانی نشده باشد، یا اگر remove()از آخرین تماس فراخوانی شده باشد next().
بنابراین، تکرار کننده برای List رایج ترین پیاده سازی است. تکرار کننده از ابتدای مجموعه تا انتهای آن می رود: به دنبال وجود عنصر بعدی است و در صورت وجود آن را برمی گرداند. یک چرخه بر اساس این الگوریتم ساده ساخته می شود for-each. پسوند آن ListIterator است. بیایید به روش های اضافی تکرار کننده لیست جاوا نگاه کنیم. شما به احتمال زیاد آنها را می شناسید:
  • void add(E e)- یک عنصر Eرا در ;List
  • boolean hasPrevious()- trueاگر Listعناصری در طول جستجوی معکوس وجود داشته باشد، باز خواهد گشت.
  • int nextIndex()- شاخص عنصر بعدی را برمی گرداند.
  • E previous()- عنصر صفحه قبلی را برمی گرداند.
  • int previousIndex()- شاخص عنصر قبلی را برمی گرداند.
  • void set(E e)- عنصری را که با آخرین فراخوانی برگردانده شده next()یا previous()با عنصر e.
بیایید به یک مثال کوچک نگاه کنیم. بیایید یک Listحاوی خطوط خوشامدگویی به دانش آموزان ایجاد کنیم:
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
اکنون یک تکرار کننده برای آن دریافت می کنیم و تمام خطوط موجود را در کنسول چاپ می کنیم:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
اکنون یک «گلوگاه» وجود خواهد داشت: مجموعه‌های جاوا، همانطور که احتمالاً می‌دانید (و اگر نمی‌دانید، آن را بفهمید)، رابط را گسترش دهید Iterable، اما این بدان معنا نیست که فقط List، Setو Queueاز یک تکرارکننده پشتیبانی می‌کند. For java Map iteratorنیز پشتیبانی می شود، اما باید فراخوانی شود Map.entrySet():
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
سپس متد next() یک شی حاوی یک جفت "کلید" - "مقدار" را برمی گرداند Entry. سپس همه چیز یکسان است با List:
while (mapIterator.hasNext()) {
    Map.Entry<String, Integer> entry = mapIterator.next();
    System.out.println("Key: " + entry.getKey());
    System.out.println("Value: " + entry.getValue());
}
شما فکر می کنید: "بس کن. ما در مورد رابط صحبت می کنیم و عنوان مقاله می گوید "الگو". یعنی الگوی تکرار کننده رابط Iterator است؟ یا اینترفیس یک الگو است؟ اگر این کلمه برای اولین بار ظاهر می شود، من به شما یک مرجع می دهم: یک الگو یک الگوی طراحی است، یک رفتار خاص که یک کلاس یا بسیاری از کلاس های به هم پیوسته باید به آن پایبند باشند. یک تکرار کننده در جاوا را می توان برای هر شیئی که ساختار داخلی آن شامل تکرار است پیاده سازی کرد و می توانید امضای متدهای مورد بحث را تغییر دهید. نکته اصلی هنگام پیاده سازی یک الگو منطقی است که کلاس باید به آن پایبند باشد. رابط تکرار کننده یک پیاده سازی خصوصی از الگوی به همین نام است که هم برای ساختارهای آماده ( List, Set, Queue, Map) و هم برای سایر ساختارها به صلاحدید برنامه نویس اعمال می شود. با گسترش رابط Iterator، شما یک الگو را پیاده سازی می کنید، اما برای پیاده سازی الگو نیازی به گسترش رابط ندارید. یک تشبیه ساده: همه ماهی ها شنا می کنند، اما هر چیزی که شنا می کند ماهی نیست. به عنوان مثال، تصمیم گرفتم ... کلمه را در نظر بگیرم. به طور خاص، یک اسم. از قسمت های پیشوند، ریشه، پسوند و پایان تشکیل شده است. برای بخش‌هایی از یک کلمه، یک رابط و کلاس‌هایی ایجاد می‌کنیم WordPartکه آن را گسترش می‌دهند Prefix, Root, Suffix и Ending:
interface WordPart {
    String getWordPart();
}

static class Root implements WordPart {

    private String part;

    public Root(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Prefix implements WordPart {

    private String part;

    public Prefix(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Suffix implements WordPart {

    private String part;

    public Suffix(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Ending implements WordPart {

    private String part;

    public Ending(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}
سپس کلاس Word(کلمه) شامل قطعات خواهد بود و علاوه بر آنها یک عدد صحیح اضافه می کنیم که تعداد قسمت های کلمه را منعکس می کند:
public class Word {

    private Root root;
    private Prefix prefix;
    private Suffix suffix;
    private Ending ending;
    private int partCount;

    public Word(Root root, Prefix prefix, Suffix suffix, Ending ending) {
        this.root = root;
        this.prefix = prefix;
        this.suffix = suffix;
        this.ending = ending;
        this.partCount = 4;
    }

    public Word(Root root, Prefix prefix, Suffix suffix) {
        this.root = root;
        this.prefix = prefix;
        this.suffix = suffix;
        this.partCount = 3;

    }

    public Word(Root root, Prefix prefix) {
        this.root = root;
        this.prefix = prefix;
        this.partCount = 2;
    }

    public Word(Root root) {
        this.root = root;
        this.partCount = 1;
    }

    public Root getRoot() {
        return root;
    }

    public Prefix getPrefix() {
        return prefix;
    }

    public Suffix getSuffix() {
        return suffix;
    }

    public Ending getEnding() {
        return ending;
    }

    public int getPartCount() {
        return partCount;
    }

    public boolean hasRoot() {
        return this.root != null;
    }

    public boolean hasPrefix() {
        return this.prefix != null;
    }

    public boolean hasSuffix() {
        return this.suffix != null;
    }

    public boolean hasEnding() {
        return this.ending != null;
    }
خوب، ما چهار سازنده اضافه بار داریم (برای سادگی، فرض کنیم فقط می توانیم یک پسوند داشته باشیم). یک اسم نمی تواند از یک پیشوند تشکیل شود، بنابراین برای سازنده با یک پارامتر، ریشه را تنظیم می کنیم. حالا بیایید یک پیاده سازی از الگوی تکرار کننده بنویسیم: WordIterator، با حذف 2 روش: hasNext()و next():
public class WordIterator implements Iterator<Word.WordPart> {

    private Word word;
    private int wordPartsCount;

    public WordIterator(Word word) {
        this.word = word;
        this.wordPartsCount = word.getPartCount();
    }

    @Override
    public boolean hasNext() {
        if (wordPartsCount == 4) {
            return word.hasPrefix() || word.hasRoot() || word.hasSuffix() || word.hasEnding();
        } else if (wordPartsCount == 3) {
            return word.hasPrefix() || word.hasRoot() || word.hasSuffix();
        } else if (wordPartsCount == 2) {
            return word.hasPrefix() || word.hasRoot();
        } else if (wordPartsCount == 1) {
            return word.hasRoot();
        }
        return false;
    }

    @Override
    public Word.WordPart next() throws NoSuchElementException {
        if (wordPartsCount <= 0) {
            throw new NoSuchElementException("No more elements in this word!");
        }

        try {
            if (wordPartsCount == 4) {
                return word.getEnding();
            }
            if (wordPartsCount == 3) {
                return word.getSuffix();
            }
            if (wordPartsCount == 2) {
                return word.getPrefix();
            }
            return word.getRoot();
        } finally {
            wordPartsCount--;
        }
    }
}
تنها چیزی که باقی می ماند این است که تکرار کننده را به کلاس اختصاص دهیم Word:
public class Word implements Iterable<Word.WordPart> {@Override
	public Iterator<WordPart>iterator() {
    		return new WordIterator(this);
	}}
اکنون بیایید یک تجزیه و تحلیل مورفمی از کلمه "عجله" انجام دهیم:
public class Main {
    public static void main(String[] args) {
        Word.Root root = new Word.Root("беж");
        Word.Prefix prefix = new Word.Prefix("пере");
        Word.Suffix suffix = new Word.Suffix("к");
        Word.Ending ending = new Word.Ending("a");

        Word word = new Word(root, prefix, suffix, ending);

        Iterator wordIterator = word.iterator();
        while (wordIterator.hasNext()) {
            Word.WordPart part = (Word.WordPart) wordIterator.next();
            System.out.println(part.getClass() + ": " + part.getWordPart());
        }
    }
}
لطفاً توجه داشته باشید که در اجرای الگوی تکرارکننده، ترتیب خروجی زیر را انتخاب کردم:
  1. پایان یافتن
  2. پسوند
  3. کنسول
  4. ریشه
هنگام طراحی تکرار کننده خود، می توانید الگوریتم تکرار را به دلخواه خود مشخص کنید. امیدوارم در مطالعه موفق باشی!
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION