JavaRush /مدونة جافا /Random-AR /نمط التكرار

نمط التكرار

نشرت في المجموعة
سنتحدث اليوم عن ماهية Iterator في Java وسبب الحاجة إليه.
نمط التكرار - 1
كما تعلم، تمتلك Java واجهة Collection رائعة تقوم بتنفيذ واجهة Iterator. اسمحوا لي أن أقوم بالحجز على الفور: لا ينبغي الخلط بين واجهة التكرار ونمط التكرار في Java! وللتوضيح، دعونا نلقي نظرة أولاً على الواجهة.
حرفيًا، يمكن ترجمة كلمة "Iterator" إلى "القوة الغاشمة " . أي أنه كيان معين يمكنه التكرار عبر جميع العناصر الموجودة في المجموعة. علاوة على ذلك، فهو يتيح لك القيام بذلك دون الخوض في البنية الداخلية وترتيب المجموعات.
لنتخيل للحظة أنه لا يوجد مكرر في Java. في هذه الحالة ، سيتعين على كل فرد أن يغوص في أعماق المجموعات ويفهم حقًا ما هو المختلف ArrayListعن . LinkedListHashSetTreeSet

الطرق التي يجب على Iterator تنفيذها

boolean hasNext()— إذا كانت لا تزال هناك قيم متبقية في الكائن القابل للتكرار (حاليًا مجموعة)، فستُرجع الطريقة trueإذا لم يكن هناك المزيد من القيم false. E next()- إرجاع العنصر التالي من المجموعة (الكائن). إذا لم يكن هناك المزيد من العناصر (لم يكن هناك اختبار hasNext()، وقمنا بالاستدعاء next()عندما وصلنا إلى نهاية المجموعة)، فسترمي الطريقة NoSuchElementException. void remove()- سيتم إزالة العنصر الذي تم الحصول عليه آخر مرة بواسطة next(). يمكن للطريقة رمي:
  • UnsupportedOperationException، إذا كان هذا المكرِّر لا يدعم الطريقة remove()(في حالة المجموعات للقراءة فقط، على سبيل المثال)
  • IllegalStateExceptionnext()إذا لم يتم استدعاء الطريقة بعد، أو إذا remove()تم استدعاؤها بالفعل منذ آخر مكالمة next().
لذا فإن مكرر القائمة هو التطبيق الأكثر شيوعًا. ينتقل المكرّر من بداية المجموعة إلى نهايتها: فهو يبحث لمعرفة ما إذا كان العنصر التالي موجودًا ويعيده إذا كان هناك عنصر. تم بناء الدورة على أساس هذه الخوارزمية البسيطة 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());
}
الآن سيكون هناك "عنق الزجاجة": مجموعات Java، كما تعلم على الأرجح (وإذا كنت لا تعرف، فاكتشف ذلك)، تعمل على توسيع الواجهة 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());
}
تعتقد: "توقف. نحن نتحدث عن الواجهة، وعنوان المقال هو "النمط". أي أن نمط التكرار هو واجهة التكرار؟ أم أن الواجهة نمط؟ إذا ظهرت هذه الكلمة لأول مرة، فأنا أعطيك مرجعًا: النمط هو نمط تصميمي، وهو سلوك معين يجب أن يلتزم به فصل دراسي أو العديد من الفئات المترابطة. يمكن تنفيذ مكرر في جافا لأي كائن يتضمن بنيته الداخلية التكرار، ويمكن تغيير توقيع الأساليب التي تمت مناقشتها. الشيء الرئيسي عند تنفيذ النمط هو المنطق الذي يجب أن يلتزم به الفصل. واجهة التكرار هي تطبيق خاص للنمط الذي يحمل نفس الاسم، ويتم تطبيقه على كل من الهياكل الجاهزة ( 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، متجاوزًا طريقتين: 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