Дизайн Патерн - ІТЕРАТОР (Iterator)

вторник, 2 ноября 2010, Andriy Buday

Уявіть, що ви розробник статегічної воєнної гри. Армія має складну структуру: вона складається із героя і трьох груп. Коли Король видає указ і ресурси щоб полікувати всіх воїнів (Герой також є воїном) Ви хочете проітерувати по всіх солдатах і викликати метод treat() на кожному інстансі. Як це можна зробити легко і без вникання в структуру Арімії?

ІТЕРАТОР

Ітератор це патерн який дозволяє доступатися почергово до елементів будь-якої колекції без вникання в суть її імплементації. Таким чином в застосуванні до нашої проблеми: Ми не хочемо перейматися структурою Армії - ми хочемо щоб SoldiersIterator пробігся по всіх солдатах.

alt text

Використання

Код нище показує використання ітератора. Як бачимо ми просто отримали інстанс ітератора 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();


Ищите нас в интернетах!

Комментарии

Свежие вакансии