Дизайн Патерн - ІТЕРАТОР (Iterator)
Уявіть, що ви розробник статегічної воєнної гри. Армія має складну структуру: вона складається із героя і трьох груп. Коли Король видає указ і ресурси щоб полікувати всіх воїнів (Герой також є воїном) Ви хочете проітерувати по всіх солдатах і викликати метод treat() на кожному інстансі. Як це можна зробити легко і без вникання в структуру Арімії?
ІТЕРАТОР
Ітератор це патерн який дозволяє доступатися почергово до елементів будь-якої колекції без вникання в суть її імплементації. Таким чином в застосуванні до нашої проблеми: Ми не хочемо перейматися структурою Армії - ми хочемо щоб SoldiersIterator пробігся по всіх солдатах.
Використання
Код нище показує використання ітератора. Як бачимо ми просто отримали інстанс ітератора SoldiersIterator. І простим циклом проходимося по всіх солдатах армії. Це дуже легко, що і є основним завданням ітератора.
var iterator = new SoldiersIterator(earthArmy); while (iterator.HasNext()) { var currSoldier = iterator.Next(); currSoldier.Treat(); }
Структура Армії
Армія складається із одного героя і може містити багато Груп, кожна із яких може містити багато солдат. Отже, як ми бачимо, сктуктура армії складна і деревовидна. Код нижче показує створення Армії:
Army earthArmy = new Army(); Group groupA = new Group(); for(int i=1; i<4; ++i) groupA.addNewSoldier(new Soldier("Alpha:" + i)); Group groupB = new Group(); for(int i=1; i<3; ++i) groupB.addNewSoldier(new Soldier("Beta:" + i)); Group groupC = new Group(); for(int i=1; i<2; ++i) groupC.addNewSoldier(new Soldier("Gamma:" + i)); earthArmy.ArmyHero = new Hero("Andriy Buday"); earthArmy.addArmyGroup(groupB); earthArmy.addArmyGroup(groupA); earthArmy.addArmyGroup(groupC);
Герой (Hero) це клас унаслідуваний від солдата(Soldier) і основна різниця така, що він має вищий рівень здоров"я.
public class Soldier { public String Name; public int Health; protected int maxHealthPoints = 100; public Soldier(String name){ Name = name; } public void treat(){ Health = maxHealthPoints; System.out.println(Name); } } public class Hero extends Soldier { protected int maxHealthPoints = 500; public Hero(String name) { super(name); } }
SoldiersIterator
То ж, якщо ми можемо рухатися по складній колекції так легко, де є вся складність? Звісно, вона прихована інкапсульована в конкретному класі ітератора.
public class SoldiersIterator { private Army _army; boolean heroIsIterated; int currentGroup; int currentGroupSoldier; public SoldiersIterator(Army army) { _army = army; heroIsIterated = false; currentGroup = 0; currentGroupSoldier = 0; } public boolean hasNext() { if(!heroIsIterated) return true; if(currentGroup < _army.ArmyGroups.size())return true; if(currentGroup == _army.ArmyGroups.size()-1) if(currentGroupSoldier < _army.ArmyGroups.get(currentGroup).Soldiers.size())return true; return false; } public Soldier next() { Soldier nextSoldier; // we still not iterated all soldiers in current group if (currentGroup < _army.ArmyGroups.size()) { if (currentGroupSoldier < _army.ArmyGroups.get(currentGroup).Soldiers.size()) { nextSoldier = _army.ArmyGroups.get(currentGroup).Soldiers.get(currentGroupSoldier); currentGroupSoldier++; } // moving to next group else { currentGroup++; currentGroupSoldier = 0; return next(); } } // hero is the last who left the battlefield else if (!heroIsIterated) { heroIsIterated = true; return _army.ArmyHero; } else { // THROW EXCEPTION HERE throw new IllegalStateException("End of colletion"); //or set all counters to 0 and start again, but not recommended } return nextSoldier; } }
Чому мій приклад не є стандартмим і класичним?
Тому що я собі поставив за завдання підкреслити головне завдання яке вирішує цей паттерн і зробити це таким чином, що можна буде все легко зрозуміти. Ще однією причиною є те, що ви можете прочитати тонни стандартних пояснень цього патерну. Головною ж різницею між моїм поясненням і іншими поясненнями є те, що стандартні є більш абстраговані. Наприклад, я створював потрібний нам ітератор таким чином:
var iterator = new SoldiersIterator(earthArmy);
Але зазвичай створення ітератора також інкапсулюється під методом Агрегата (як GetEnumerator в .NET). Мій код міг б виглядати так:
AbstractIterator iterator = AbstractArmy.GetSoldiersIterator();
В світі .NET ми маємо інтерфейси IEnumerable і IEnumerator, які нам допомагають у використанні цього патерну.
var list = new List<int>(); //GetEnumerator is method of IEnumerator (Aggregate) var enumerator = list.GetEnumerator(); //MoveNext method of IEnumerable (Iterator) enumerator.MoveNext();
http://andriybuday.blogspot.com/2010/01/design-patterns.html