Iterator

2022. 3. 15. 00:00CSharp/Design Pattern

반응형

기반이 되는 표현을 노출시키지 않고 연속적으로 객체 요소에 접근하는 방법을 제공하는 것

다시말해 aggregate 유형에 무관한 동일 순차 접근 방법을 제공하는것이며 , 여기서 aggregate란 반복자객체를 생성하기위한 인터페이스를 정의하는것이고 iterator 란 요소에 접근 할 수 있고 순회 할 수 있는 인터페이스를 정의하는것이다.

 

 

문제

 

컬렉션의 각 요소에 순차적으로 액세스할 수 있는 방법이 필요할때list 같은 collection 이라면 쉬운 작업일 수 있으나 복잡한 트리 구조라면 이야기가 달라진다.또 해당 tree 를 깊이 우선으로 탐색 할지 너비 우선으로 탐색 할지에 따라 방법도 달라진다. 

 

https://refactoring.guru/design-patterns/iterator

 

 

해결책

컬렉션의 순회 동작 자체를 iterator 라는 별도의 객체로 추출하는 것

https://refactoring.guru/design-patterns/iterator

Iterator 는 다양한 순회 알고리즘을 구현하고 여러 반복자 개체가 같은 컬렉션을 동시에 순회할 수 있다.

또한 현재 위치 및 끝까지 남은 요소 수와 같은 모든 탐색 세부 정보를 캡슐화

 

 

class diagram

적용

컬렉션의 내부에 복잡한 데이터 구조가 있지만 그 복잡성을 클라이언트로부터 숨기고 싶은 경우

앱 전체에서 순회 코드의 중복을 줄이고 싶을때

코드가 다른 데이터 구조를 순회할 수 있기를 원하거나 이러한 구조의 유형을 미리 알 수 없는 경우 

 

간단하게 주(weeks) 라는 aggregate (collection) 를 만들고 해당 aggregate 을 순회하는 interator를 만들어 보자

Iterator 정의

public interface IWeeksIterator //Iterator interface
{
    string Current { get; }

    bool MoveNext();
}
public class WeeksIterator : IWeeksIterator //Iterator object
{
    private readonly string[] weeks;
    private int position;

    public WeeksIterator(string[] weeks)
    {
        this.weeks = weeks;
        this.position = -1;
    }

    public string Current => weeks[position];

    public bool MoveNext()
    {
        if (++position == weeks.Length) return false;
        return true;
    }
}

Aggregate (Collection) 정의

public class Weeks //Aggregate object
{
    private string[] weeks = new string[]{
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday"
        };

    public IWeeksIterator GetWeeksIterator()
    {
        //creates an Iterator object
        return new WeeksIterator(weeks);
    }
}

사용법

var weeks = new Weeks();
var iterator = weeks.GetWeeksIterator();
while (iterator.MoveNext())
{
    Console.WriteLine(iterator.Current);
}
//output
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday

 

위와 같은 경우 client 쪽의 코드를 수정하지 않고 순회 하는 방식을 바꾸려면

다른 iterator 를  구현 하면 된다. 

// 평일용 iterator
public class WeekDaysIterator : IWeeksIterator
{
    private readonly string[] weeks;
    private int position;

    public WeekDaysIterator(string[] weeks)
    {
        this.weeks = weeks;
        this.position = -1;
    }

    public string Current => weeks[position];

    public bool MoveNext()
    {
        if (++position == (weeks.Length - 2)) return false;
        return true;
    }
}

WeeksIterator 를 WeekDaysIterator 로 변경

//output
Monday
Tuesday
Wednesday
Thursday
Friday

또한 C# 에서는 yield 구문을 통해 아주 간단하게 iterator 를 구현 할 수 있다. 

 

yield 구문과 IEnumerable 을 통하여 구현 하는 Iterator

public class Weeks 
{
    private string[] weeks = new string[]{
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday"
        };

    public IEnumerable GetWeeksIterator()
    {
        foreach (var item in weeks)
        {
            yield return item;
        }
    }

    public IEnumerable GetWeekDaysIterator()
    {
        for (int i = 0; i < (weeks.Length - 2); i++)
        {
            yield return weeks[i];
        }
    }
}

사용법

var weeks = new Weeks();
foreach (var item in weeks.GetWeeksIterator())
{
    Console.WriteLine(item);
}

Console.WriteLine();

foreach (var item in weeks.GetWeekDaysIterator())
{
    Console.WriteLine(item);
}
// output
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday

Monday
Tuesday
Wednesday
Thursday
Friday

weeks (aggregate) 가 IEnumerable 을 직접 implements 하는 경우

public class Weeks : IEnumerable
{
    private string[] weeks = new string[]{
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday"
        };


    public IEnumerator GetEnumerator()
    {
        foreach (var item in weeks)
        {
            yield return item;
        }
        //for (int i = 0; i < (weeks.Length - 2); i++)
        //{
        //    yield return weeks[i];
        //}
    }
}

사용법

var weeks = new Weeks();
foreach (var item in weeks)
{
    Console.WriteLine(item);
}
//output
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday

 

관련영상

https://youtu.be/FQNvcHF21D8

 

 

 

반응형

'CSharp > Design Pattern' 카테고리의 다른 글

State  (0) 2022.03.17
Memento  (0) 2022.03.16
Chain of Responsibility  (0) 2022.03.14
Proxy  (0) 2022.03.11
Flyweight  (0) 2022.03.11