الگوی Iterator

الگوی iterator به دلیل استفاده زیادی که دارد بصورت built-in در فریم ورک هایی که استفاده می کنیم پیاده سازی شده و وجود دارد.الگوی iteratorدر مورد data structureهای مختلف، می تونیم در مورد یک array به عنوان یک list صحبت کنیم. همینطور در مورد stack, linkedList و structureهای مختلف می تونیم فکر کنیم.تمام این structureها، المان هایی را درون خود دارند که قابل iterate کردن هستند. منظور از iterate، چرخیدن به صورت sequential در بین آیتم ها می باشد.اما از آنجایی که این structureها، representationهای متفاوتی دارند، client برای iterate کردن آیتم ها باید جزئیات آن ها را داشته باشد و نسبت به آن ها آگاهی داشته باشد. مشکلی که وجود دارد این است که client به representation وابسته می شود. راه دیگر این است که از خود آن structure بخواهیم آیتم بعدی را به ما بدهد. بنابراین iterator به دنبال ارائه روشی است که client بدون داشتن اطلاعات در مورد representation، بتواند المان های آن را iterate کند.فرض کنید کلاسی به نام FruitBasket داریم که در نقش aggregate می باشد و یکسری مقادیر درون خود دارد. همچنین یک کلاس و یک اینترفیس به نام های Fruit و IIterator هم وجود دارد.public class FruitBasket { private string[] _fruits = new string[] { &quotApple&quot, &quotOrange&quot, &quotBanana&quot }; } public class Fruit { } public interface IIterator<T> { bool IsDone(); void Next(); T CurrentItem(); }چیزی که client از بیرون می بیند، یک Iterator interface است. این interface، رفتارهای مربوط به iterator را درون خود مدل می کند. client با استفاده از این interface، فارغ از اینکه در مورد چه آبجکتی صحبت می شود، می تواند iteration را انجام دهد.IIterator<string> iterator = null; while(iterator.IsDone() == false) { var item = iterator.CurrentItem(); iterator.Next(); }مساله این است که iterator چطور به دست client می رسد؟در الگوی iterator، در ساختار aggregate، می تونیم پیاده سازی از iterator را بنویسیم و در آن منطق مربوط به iterate کردن قرار دارد. public interface IBasket<T> { IIterator<T> CreateIterator(); } public class FruitBasket : IBasket<string> { private string[] _fruits = new string[] { &quotApple&quot, &quotOrange&quot, &quotBanana&quot }; public IIterator<string> CreateIterator() { return new FruitIterator(_fruits); } } public class FruitIterator : IIterator<string> { private readonly string[] _fruits; private int _index; public FruitIterator(string[] fruits) { _fruits = fruits; } public string CurrentItem() { return _fruits[_index]; } public bool IsDone() { return _index == _fruits.Length; } public void Next() { _index++; } }بنابراین client فقط iterator را می شناسد و فارغ از اینکه representation چه چیزی باشد، از طریق interface، آن را iterate می کند. var basket = GetBasket(); var iterator = basket.CreateIterator(); while (iterator.IsDone() == false) { var item = iterator.CurrentItem(); iterator.Next(); }private static IBasket<string> GetBasket() { return new FruitBasket(); }هر subtype از این aggregate که در اینجا IBasket است، بسته به منطقی که دارد می تواند iteration خود را درست کند. به این مفهوم polymorphic iteration گفته می شود.به این ترتیب، iterator را در غالب یک abstraction تعریف کردیم و این اجازه را دادیم که هر aggregate پیاده سازی های خود را داشته باشد و client تنها آن iterator را بشناسد. در دات نت، یک interface به نام IEnumerable وجود دارد که از طریق متدی به نام GetEnumerator، یک object از نوع IEnumerator را بر می گرداند. IEnumerable و IEnumerator به ترتیب در نقش aggregate و iterator هستند.var list = new List<string> { &quottest1&quot, &quottest2&quot, &quottest3&quot }; var iterator = list.GetEnumerator(); while (iterator.MoveNext()) { var item = iterator.Current; iterator.MoveNext(); }چرا ما به عنوان برنامه نویس دات نت، همچین کدی را نمی نویسیم؟ استفاده از foreach به عنوان یک موضوع کامپایلری، این نیاز را برای ما برآورده می کند. به طور خلاصه، object structure در این الگو را به عنوان variability در نظر می گیریم و وجود یک sequence یا enumerable که قابل شمارش است را به عنوان commonality در نظر می گیریم.