النمط التكراري المعماري في الكون سويفت

يعد "التكرار" أحد أنماط التصميم التي لا يلاحظها المبرمجون في أغلب الأحيان ، لأن تنفيذها ، كقاعدة عامة ، يتم تضمينه مباشرةً في الأدوات القياسية للغة البرمجة. ومع ذلك ، يعد هذا أيضًا أحد الأنماط السلوكية الموضحة في كتاب "Gang of Four" ، و "GoF" ، و "أنماط التصميم: عناصر من البرنامج الموجه لإعادة الاستخدام" ، وفهمها لا يؤلم الجهاز الخاص به ، وأحيانًا يمكن أن يساعد في شيء ما.

"التكرار" هو وسيلة للوصول المتسلسل إلى جميع عناصر كائن مركب (على وجه الخصوص ، أنواع الحاويات ، مثل مجموعة أو مجموعة).

أدوات اللغة القياسية


إنشاء نوع من الصفيف :

let numbersArray = [0, 1, 2] 

... ثم "المشي" من خلال ذلك في دورة :

 for number in numbersArray { print(number) } 

... يبدو وكأنه إجراء طبيعي للغاية ، خاصة بالنسبة للغات البرمجة الحديثة مثل سويفت . ومع ذلك ، وراء الكواليس من هذا الإجراء البسيط هو رمز ينفذ مبادئ نمط التكرار.

في "Swift" ، من أجل التمكن من "تكرار" المتغير باستخدام التكرار ، يجب أن يقوم النوع المتغير بتطبيق بروتوكول Sequence . من بين أشياء أخرى ، يتطلب هذا البروتوكول من النوع أن يكون لديه Iterator associatedtype ، والذي بدوره يجب أن ينفذ متطلبات بروتوكول IteratorProtocol ، وكذلك makeIterator() method factory factory الذي يعرض "محدد" محدد لهذا النوع:

 protocol Sequence { associatedtype Iterator : IteratorProtocol func makeIterator() -> Self.Iterator // Another requirements go here… } 

يحتوي بروتوكول IteratorProtocol ، بدوره ، على طريقة واحدة فقط - next() ، والتي تُرجع العنصر التالي في التسلسل:

 protocol IteratorProtocol { associatedtype Element mutating func next() -> Self.Element? } 

يبدو مثل "الكثير من التعليمات البرمجية المعقدة" ، لكنه في الواقع ليس كذلك. أدناه سوف نرى هذا.

يقوم نوع Array بتنفيذ بروتوكول Sequence (ولكن ليس بشكل مباشر ، ولكن من خلال سلسلة ميراث البروتوكول : يرث MutableCollection متطلبات Collection ، والأخير يرث متطلبات Sequence ) ، لذلك يمكن مثيلاته ، على وجه الخصوص ، "التكرار" باستخدام for -cycles.

أنواع مخصصة


ما الذي يجب القيام به لتتمكن من تكرار نوعك الخاص؟ كما يحدث في كثير من الأحيان ، فمن الأسهل لإظهار مثال.

افترض أن هناك نوعًا يمثل رفًا للكتب يخزن مجموعة معينة من مثيلات الفصل ، والذي بدوره يمثل كتابًا:

 struct Book { let author: String let title: String } struct Shelf { var books: [Book] } 

لتكون قادرًا على "تكرار" مثيل لفئة Shelf ، يجب أن تفي هذه الفئة بمتطلبات بروتوكول Sequence . على سبيل المثال ، سيكون تطبيق أسلوب makeIterator() كافياً فقط ، خاصة وأن متطلبات البروتوكول الأخرى لها تطبيقات افتراضية . يجب أن تقوم هذه الطريقة بإرجاع مثيل لنوع يقوم بتنفيذ بروتوكول IteratorProtocol . لحسن الحظ ، في حالة سويفت ، هذا رمز بسيط للغاية:

 struct ShelfIterator: IteratorProtocol { private var books: [Book] init(books: [Book]) { self.books = books } mutating func next() -> Book? { // TODO: Return next underlying Book element. } } extension Shelf: Sequence { func makeIterator() -> ShelfIterator { return ShelfIterator(books: books) } } 

تم الإعلان عن تغيير الطريقة next() الخاصة بنوع ShelfIterator ، لأن مثيل الكتابة يجب أن يخزن بطريقة ما الحالة المقابلة للتكرار الحالي:

 mutating func next() -> Book? { defer { if !books.isEmpty { books.removeFirst() } } return books.first } 

يعرض خيار التطبيق هذا دائمًا العنصر الأول في التسلسل ، أو nil إذا كان التسلسل فارغًا. defer "لف" كتلة التأجيل برمز لتغيير المجموعة التكرارية ، مما يزيل عنصر آخر خطوة تكرارية فور عودة الطريقة.

مثال للاستخدام:

 let book1 = Book(author: ". ", title: "") let book2 = Book(author: ". ", title: " ") let book3 = Book(author: ". ", title: " ") let shelf = Shelf(books: [book1, book2, book3]) for book in shelf { print("\(book.author) – \(book.title)") } /* .  –  .  –   .  –   */ 

بسبب تستند جميع الأنواع المستخدمة (بما في ذلك Shelf الأساسي Shelf ) إلى دلالات القيم (على عكس المراجع) ، فلا داعي للقلق بشأن قيمة المتغير الأصلي الذي يتم تغييره أثناء التكرار. عند التعامل مع الأنواع استنادًا إلى دلالات الارتباط ، يجب أن تؤخذ هذه النقطة في الاعتبار وتؤخذ في الاعتبار عند إنشاء برامج التكرار الخاصة بك.

وظائف الكلاسيكية


يمكن لـ "التكرار" الكلاسيكي الموصوف في كتاب "Gang of of Four" ، بالإضافة إلى إرجاع العنصر التالي للتسلسل القابل للتكرار ، في أي وقت أيضًا إرجاع العنصر الحالي في عملية التكرار ، العنصر الأول في التسلسل التكراري وقيمة "العلم" الذي يشير إلى ما إذا كان لا يزال هناك العناصر في تسلسل مكرر نسبة إلى خطوة التكرار الحالية.

بالطبع ، سيكون من السهل الإعلان عن بروتوكول وبالتالي توسيع قدرات IteratorProtocol القياسية:

 protocol ClassicIteratorProtocol: IteratorProtocol { var currentItem: Element? { get } var first: Element? { get } var isDone: Bool { get } } 

يتم إرجاع العناصر الأولى والحالية اختياري منذ قد يكون تسلسل المصدر فارغًا.

خيار التنفيذ البسيط:

 struct ShelfIterator: ClassicIteratorProtocol { var currentItem: Book? = nil var first: Book? var isDone: Bool = false private var books: [Book] init(books: [Book]) { self.books = books first = books.first currentItem = books.first } mutating func next() -> Book? { currentItem = books.first books.removeFirst() isDone = books.isEmpty return books.first } } 

في الوصف الأصلي للنمط ، يغير الأسلوب next() الحالة الداخلية للتكرار للانتقال إلى العنصر التالي ويكون من النوع Void ، ويتم إرجاع العنصر الحالي بواسطة الأسلوب currentElement() . في بروتوكول IteratorProtocol ، تكون هاتان الوظيفتان كما لو تم دمجهما في واحدة.

الحاجة إلى الطريقة first() مشكوك فيها أيضًا ، لأن لا يغير التكرار التسلسل الأصلي ، ولدينا دائمًا فرصة الوصول إلى العنصر الأول (إن وجد ، بالطبع).

ونظرًا لأن الطريقة next() تُرجع nil عند انتهاء التكرار ، فإن الطريقة isDone() أيضًا تصبح عديمة الفائدة.

ومع ذلك ، للأغراض الأكاديمية ، من الممكن تمامًا التوصل إلى وظيفة يمكنها استخدام الوظيفة الكاملة:

 func printShelf(with iterator: inout ShelfIterator) { var bookIndex = 0 while !iterator.isDone { bookIndex += 1 print("\(bookIndex). \(iterator.currentItem!.author) – \(iterator.currentItem!.title)") _ = iterator.next() } } var iterator = ShelfIterator(books: shelf.books) printShelf(with: &iterator) /* 1. .  –  2. .  –   3. .  –   */ 

المعلنة iterator يتم الإعلان عنها بسبب تتغير حالتها الداخلية أثناء تنفيذ الوظيفة. وعندما يتم استدعاء الوظيفة ، يتم نقل مثيل التكرار ليس مباشرة من خلال قيمته الخاصة ، ولكن حسب المرجع.

لا يتم استخدام نتيجة استدعاء الأسلوب next() ، محاكاة غياب القيمة المرجعة لتطبيق كتاب مدرسي.

بدلا من الاستنتاج


يبدو أن هذا هو كل ما أردت قوله هذه المرة. كل رمز جميل ومتعمد كتابته!

مقالاتي الأخرى عن أنماط التصميم:

Source: https://habr.com/ru/post/ar437614/


All Articles