“Патерн Вівторка” #10: Ітератор (Iterator)
|Ітератор це патерн який дозволяє доступатися почергово до елементів будь-якої колекції без вникання в суть її імплементації.
Таким чином в застосуванні до нашої проблеми:
Ми не хочемо перейматися структурою Армії – ми хочемо щоб SoldiersIterator пробігся по всіх солдатах.
Використання
Код нище показує використання ітератора. Як бачимо ми просто отримали інстанс ітератора SoldiersIterator. І простим циклом проходимося по всіх солдатах армії. Це дуже легко, що і є основним завданням ітератора.
var iterator = new SoldiersIterator(earthArmy);
while (iterator.HasNext())
{
var currSoldier = iterator.Next();
currSoldier.Treat();
}
Структура Армії
Армія складається із одного героя і може містити багато Груп, кожна із яких може містити багато солдат. Отже, як ми бачимо, сктуктура армії складна і деревовидна. Код нижче показує створення Армії:
var andriybuday = new Hero("Andriy Buday");
var earthArmy = new Army(andriybuday);
var groupA = new Group();
for (int i = 1; i < 4; ++i)
groupA.AddNewSoldier(new Soldier("Alpha:" + i));
var groupB = new Group();
for (int i = 1; i < 3; ++i)
groupB.AddNewSoldier(new Soldier("Beta:" + i));
var groupC = new Group();
for (int i = 1; i < 2; ++i)
groupC.AddNewSoldier(new Soldier("Gamma:" + i));
earthArmy.AddArmyGroup(groupB);
earthArmy.AddArmyGroup(groupA);
earthArmy.AddArmyGroup(groupC);
Герой (Hero) це клас унаслідуваний від солдата(Soldier) і основна різниця така, що він має вищий рівень здоров”я.
public class Soldier
{
public String Name;
public int Health;
private const int SoldierHealthPoints = 100;
protected virtual int MaxHealthPoints { get { return SoldierHealthPoints; } }
public Soldier(String name)
{
Name = name;
}
public void Treat()
{
Health = MaxHealthPoints;
Console.WriteLine(Name);
}
}
public class Hero : Soldier
{
private const int HeroHealthPoints = 500;
protected override int MaxHealthPoints { get { return HeroHealthPoints; } }
public Hero(String name)
: base(name)
{
}
}
SoldiersIterator
То ж, якщо ми можемо рухатися по складній колекції так легко, де є вся складність? Звісно, вона прихована інкапсульована в конкретному класі ітератора.
public class SoldiersIterator
{
private readonly Army _army;
private bool _heroIsIterated;
private int _currentGroup;
private int _currentGroupSoldier;
public SoldiersIterator(Army army)
{
_army = army;
_heroIsIterated = false;
_currentGroup = 0;
_currentGroupSoldier = 0;
}
public bool HasNext()
{
if (!_heroIsIterated) return true;
if (_currentGroup < _army.ArmyGroups.Count) return true;
if (_currentGroup == _army.ArmyGroups.Count - 1)
if (_currentGroupSoldier < _army.ArmyGroups[_currentGroup].Soldiers.Count) return true;
return false;
}
public Soldier Next()
{
Soldier nextSoldier;
// we still not iterated all soldiers in current group
if (_currentGroup < _army.ArmyGroups.Count)
{
if (_currentGroupSoldier < _army.ArmyGroups[_currentGroup].Soldiers.Count)
{
nextSoldier = _army.ArmyGroups[_currentGroup].Soldiers[_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 Exception("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
var list = new List();
//GetEnumerator is method of IEnumerator (Aggregate)
var enumerator = list.GetEnumerator();
//MoveNext method of IEnumerable (Iterator)
enumerator.MoveNext();