추상화된(Interface)를 통해서 다른 구조의 데이터여도 반복 기능을 통일시킨 패턴입니다.
행동 패턴(Behavioral Design Patterns)중 하나이죠.
장점
- 단일 책임 원칙을 만족하여 복잡한 순회 알고리즘들을 클래스들로 만들어 관리할 수 있습니다.
- 개방/폐쇄 원칙을 만족하여 새로운 유형의 컬렉션들과 반복자들을 구현할 수 있으며 변경없이 기존의 코드에 전달할 수 있습니다.
- 각각의 반복자 객체는 고유한 상태를 가져 컬렉션을 다양하게 조회할 수 있습니다.
- 조회를 지연 및 지속 시킬 수 있습니다.
단점
- 단순한 동작의 경우 반복자 패턴은 과할 수 있습니다.
- 일부 클래스의 경우 직접 조회하는 것보다 비효율적일 수 있습니다.
C#으로 Console 프로젝트를 하나 생성해서 할 것입니다.
목표는 학생들의 이름을 순회하여 출력하겠습니다.
프로젝트의 tree구조는 아래와 같습니다.
Iterator (Project)
|- Program.cs (Main)
|- Iterator.cs (Iterator Interface)
|- IteratorAggregate.cs (Iterable Collection Interface)
|- OrderIterator.cs (Concreate Iterator)
|- StudentsCollection.cs (Concreate Iterable Collection Interface)
|- Student.cs (data)
Iterator.cs
using System.Collections;
namespace Iterator
{
public abstract class Iterator : IEnumerator
{
object IEnumerator.Current { get { return GetCurrentItem(); } }
public abstract int GetIndex();
public abstract object GetCurrentItem();
public abstract bool MoveNext();
public abstract void Reset();
}
}
C#뿐만 아니라 다양한 언어에서 IEnumerator, IEnumerable돠 같은 혹은 비슷한 기능을 지원합니다.
IEnumerator와 IEnumerable를 구현해서 사용하면 foreach에서 사용 가능한 사용자 정의 컬렉션이 됩니다.
보다 간편하게 사용하기 위해 abstract클래스로 만들어서 사용하겠습니다.
IteratorAggregate.cs
using System.Collections;
namespace Iterator
{
public abstract class IteratorAggregate : IEnumerable
{
public abstract IEnumerator GetEnumerator();
}
}
IEnumerable를 통해 foreach 구문에서 필요한 IEnumerator를 반환하는 메소드를 구현 시켜야합니다.
이 역시 abstract클래스로 넘기겠습니다.
OrderIterator.cs
namespace Iterator
{
public class OrderIterator : Iterator
{
private StudentsCollection _collection;
private int _position = -1;
private bool _reverse = false;
public OrderIterator(StudentsCollection collection, bool reverse = false)
{
this._collection = collection;
this._reverse = reverse;
if (reverse)
{
this._position = collection.getItems().Count;
}
}
public override object GetCurrentItem()
{
return this._collection.getItems()[_position];
}
public override int GetIndex()
{
return this._position;
}
public override bool MoveNext()
{
int updatedPosition = this._position + (this._reverse ? -1 : 1);
if (updatedPosition >= 0 && updatedPosition < this._collection.getItems().Count)
{
this._position = updatedPosition;
return true;
}
else
{
return false;
}
}
public override void Reset()
{
this._position = this._reverse ? this._collection.getItems().Count - 1 : 0;
}
}
}
전에 만든 Iterator를 상속받아 구현해줍니다.
StudentsCollection.cs
using System.Collections;
using System.Collections.Generic;
namespace Iterator
{
public class StudentsCollection : IteratorAggregate
{
private List<Student> students = new List<Student>();
private bool direction = false;
public void ReverseDirection()
{
direction = !direction;
}
public List<Student> getItems()
{
return students;
}
public void AddItem(Student item)
{
this.students.Add(item);
}
public override IEnumerator GetEnumerator()
{
return new OrderIterator(this, direction);
}
}
}
전에 만든 IteratorAggregate를 상속받아 구현해줍니다. Student를 사용할 예정이기에 Student List를 내부에 구현해주고 direction을 통한 방향 제어를 해줍니다.
Student.cs
namespace Iterator
{
public class Student
{
public string name { get; set; }
public Student(string name)
{
this.name = name;
}
}
}
이름을 출력할 것이기 때문에 일단 이름 정보만 담아줍니다.
Program.cs
using System;
namespace Iterator
{
class Program
{
static void Main(string[] args)
{
StudentsCollection collection = new StudentsCollection();
collection.AddItem(new Student("Alan"));
collection.AddItem(new Student("Cassidy"));
collection.AddItem(new Student("Cody"));
collection.AddItem(new Student("Claude"));
collection.AddItem(new Student("Conrad"));
collection.AddItem(new Student("Daniel"));
collection.AddItem(new Student("David"));
collection.AddItem(new Student("Grace"));
collection.AddItem(new Student("Haley"));
collection.AddItem(new Student("Hannah"));
collection.AddItem(new Student("Heather"));
Console.WriteLine("*****************");
foreach (Student element in collection)
{
Console.WriteLine(element.name);
}
Console.WriteLine("\n*******Reverse******");
collection.ReverseDirection();
foreach (Student element in collection)
{
Console.WriteLine(element.name);
}
}
}
}
정렬된 학생들을 StudentsCollection(사용자 정의 컬렉션)에 넣어 준 뒤 foreach를 통해 순회를 해줍니다.
이후에 역순으로 바꿔 다시 조회를 해봤습니다.
결과는 아래와 같이 잘 출력됨을 확인할 수 있습니다.
사용자 정의 컬렉션이 필요한 경우에 사용하게 되는 패턴입니다.
주로 C/C++에서 동일한 반복문을 사용할 때 자주 사용합니다.
'Design Pattern' 카테고리의 다른 글
[Design Pattern] 12. Adapter Pattern(어댑터 패턴) C# (0) | 2023.04.01 |
---|---|
[Design Pattern] 11. Memento Pattern(메멘토 패턴) C# (0) | 2023.03.31 |
[Design Pattern] 9. Visitor Pattern(방문자 패턴) C# (0) | 2023.03.29 |
[Design Pattern] 8. Tamplate Method Pattern(탬플릿 메소드 패턴) C# (0) | 2023.03.28 |
[Design Pattern] 7. Command Pattern(명령 패턴) C# (0) | 2023.03.27 |