1. 반복자 패턴이란?
- 일련의 데이터 집합에 대해 순차적인 접근(순회)을 지원하는 패턴.
- 컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있는 방법을 제공하는 패턴.
- 반복자를 사용하여 컨테이너를 가로지르며 컨테이너의 요소들에 접근하는 디자인 패턴.
- 컬렉션의 요소들을 순회하는 패턴.
2. 반복자 패턴을 사용하는 경우
- 해시나 트리구조와 같이 저장순서가 정해지지 않고 적재된 데이터들을 순회하는 방법을 정하는 경우
- 컬렉션에 상관없이 객체 접근 순회 방식을 통일하고자 할 경우
- 컬렉션을 순회하는 다양한 방법을 지원하고 싶은 경우
- 컬렉션의 복잡한 내부 구조를 클라이언트로부터 숨기고 싶은 경우(편의 + 보안)
- 데이터 저장 컬렉션 종류가 변경 가능성이 있는 경우
3. 반복자 패턴의 장점
1) 일관성 - 일관된 이터레이터 인터페이스를 사용해 여러 형태의 컬렉션에 대해 동일한 순회 방법을 제공함.
2) 은닉성 - 컬렉션의 내부 구조 및 순회 방식을 알지 않아도 됨.
3) 분리성 - 집합체의 구현부와 처리부를 반복자 객체로 분리해 결합도를 줄일 수 있음.
4) 단일 책임 원칙(SRP) 준수 - 순회 알고리즘을 별도의 반복자 객체에 추출하여 각 클래스의 책임을 분리하여 SRP를 준수한다.
5) 개방 폐쇄 원칙(OCP) 준수 - 데이터 저장 컬렉션 종류가 변경되어도 클라이언트 구현 코드는 손상되지 않아 수정에는 닫혀있어 OCP를 준수한다.
4. 반복자 패턴의 단점
1) 클래스가 늘어나고 복잡도가 증가한다.
2) 구현 방법에 따라 캡슐화를 위배할 수 있다.
3) 내부 표현을 노출시키면 컬렉션의 종류가 바뀔 때마다 클라이언트 코드를 변경해주어야 함.
5. 코드 구현
- 구조
1) Aggregate(인터페이스)
- Concrete Iterator 객체를 반환하는 인터페이스 제공
2) Concrete Aggregate(클래스)
- 여러 요소들이 이루어져 있는 데이터들의 집합
3) Iterator(인터페이스)
- Concrete Iterator 객체를 만듬
- 집합체 내의 요소들을 순서대로 검색하기 위한 인터페이스 제공
4) Concrete Iterator(클래스)
- 반복자 객체
- concrete Aggregate가 구현한 메서드로부터 생성되며, Concrete Aggregate의 컬렉션을 참조하여 순회함
- 코드 구현(C#)
1) 패턴 적용 전
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace StudyCSharp
{
// 공부
class Study
{
// 과목명
string _name;
public string Name { get { return _name; } }
// 흥미도(10점 만점)
int _interest;
public int Interest { get { return _interest; } }
// 마지막 공부일자 - 오늘 날짜 23.12.15 기준
DateTime _lastTime;
public DateTime LastTime { get { return _lastTime; } }
public Study(string name, int interest, DateTime lastTime)
{
this._name = name;
this._interest = interest;
this._lastTime = lastTime;
}
}
// 시간표
class TimeTable
{
List<Study> studyList = new List<Study>(); // 공부목록
public void addStudy(string name, int interest, DateTime lastTime)
{
this.studyList.Add(new Study(name, interest, lastTime));
}
public List<Study> getStudyList()
{
return studyList;
}
}
class Program
{
static void Main(string[] args)
{
// 시간표 생성
TimeTable timeTable = new TimeTable();
// 시간표 목록 작성
timeTable.addStudy("국어", 3, new DateTime(2023, 12, 12));
timeTable.addStudy("수학", 8, new DateTime(2023, 12, 14));
timeTable.addStudy("체육", 10, DateTime.Today);
timeTable.addStudy("영어", 4, new DateTime(2023, 12, 11));
timeTable.addStudy("과학", 9, new DateTime(2023, 12, 13));
// 시간표 저장
List<Study> studyList = timeTable.getStudyList();
// 시간표 작성 순으로 조회
for (int i = 0; i < studyList.Count; i++)
{
Study study = studyList[i];
Console.WriteLine(study.Name + " / " + study.Interest + " / " + study.LastTime);
}
Console.WriteLine("----------------------------------------------");
// 흥미 낮은순으로 시간표 정렬
List<Study> InterestOrderList = studyList.OrderBy(i => i.Interest).ToList();
// 흥미 순으로 시간표 조회
for (int i = 0; i < InterestOrderList.Count; i++)
{
Study study = InterestOrderList[i];
Console.WriteLine(study.Name + " / " + study.Interest + " / " + study.LastTime);
}
Console.WriteLine("----------------------------------------------");
// 마지막에 공부한 과목부터 차례로 시간표 정렬
List<Study> TimeOrderList = studyList.OrderByDescending(i => i.LastTime).ToList();
// 시간 순으로 시간표 조회
for (int i = 0; i < TimeOrderList.Count; i++)
{
Study study = TimeOrderList[i];
Console.WriteLine(study.Name + " / " + study.Interest + " / " + study.LastTime);
}
Console.WriteLine("----------------------------------------------");
}
}
}
- 실행 결과
2) 패턴 적용 후
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace StudyCSharp
{
abstract class Aggregate
{
public abstract Iterator CreateIterator();
}
abstract class Iterator
{
public abstract Study First();
public abstract Study Next();
public abstract Study CurrentItem();
public abstract bool IsDone();
}
// Data Type
class Study
{
// 과목명
string _name;
public string Name { get { return _name; } }
// 흥미도(10점 만점)
int _interest;
public int Interest { get { return _interest; } }
// 마지막 공부일자 - 오늘 날짜 23.12.16 기준
DateTime _lastTime;
public DateTime LastTime { get { return _lastTime; } }
public Study(string name, int interest, DateTime lastTime)
{
this._name = name;
this._interest = interest;
this._lastTime = lastTime;
}
}
// Concrete Aggregate
class TimeTable : Aggregate
{
private List<Study> _timeTable = new List<Study> (); // 공부 목록
public void addTimeTable (string name, int interest, DateTime lastTime)
{
// 리스트에 항목 추가
this._timeTable.Add(new Study(name, interest, lastTime));
}
public List<Study> getTimeTable()
{
// 리스트를 불러옴
return _timeTable;
}
public void setTimeTable(List<Study> timeTable)
{
_timeTable = timeTable;
}
public override Iterator CreateIterator()
{
return new SortIterator(this);
}
public int Count
{
get { return _timeTable.Count; }
}
public Study this[int index]
{
get { return _timeTable[index]; }
set { _timeTable.Insert(index, value); }
}
}
// Concrete Iterator
class SortIterator : Iterator
{
protected TimeTable _timeTable = new TimeTable();
private int _current = 0;
public SortIterator(TimeTable timeTable)
{
this._timeTable = timeTable;
}
public void IntersetIterator(TimeTable timeTable)
{
// 흥미를 기준으로 TimeTable을 정렬
this._timeTable.setTimeTable(timeTable.getTimeTable().OrderBy(i => i.Interest).ToList());
}
public void DateIterator(TimeTable timeTable)
{
// 날짜를 기준으로 TimeTable 정렬
this._timeTable.setTimeTable(timeTable.getTimeTable().OrderBy(i => i.LastTime).ToList());
}
public override Study First()
{
return _timeTable[0];
}
public override Study Next()
{
Study ret = null;
if (_current < _timeTable.Count - 1)
{
ret = _timeTable[++_current];
}
return ret;
}
public override Study CurrentItem()
{
return _timeTable[_current];
}
public override bool IsDone()
{
return _current >= _timeTable.Count;
}
}
// Concrete Iterator
class InterestIterator : SortIterator
{
public InterestIterator(TimeTable timeTable) : base(timeTable)
{
// 흥미를 기준으로 TimeTable을 정렬
this._timeTable.setTimeTable(timeTable.getTimeTable().OrderBy(i => i.Interest).ToList());
}
}
// Concrete Iterator
class DateIterator : SortIterator
{
public DateIterator(TimeTable timeTable) : base(timeTable)
{
// 날짜를 기준으로 TimeTable 정렬
this._timeTable.setTimeTable(timeTable.getTimeTable().OrderByDescending(i => i.LastTime).ToList());
}
}
class Program
{
static void Main(string[] args)
{
// 시간표 생성
TimeTable timeTable = new TimeTable();
// 시간표 목록 작성
timeTable.addTimeTable("국어", 3, new DateTime(2023, 12, 12));
timeTable.addTimeTable("수학", 8, new DateTime(2023, 12, 14));
timeTable.addTimeTable("체육", 10, DateTime.Today);
timeTable.addTimeTable("영어", 4, new DateTime(2023, 12, 11));
timeTable.addTimeTable("과학", 9, new DateTime(2023, 12, 13));
// 흥미 기준 정렬
SortIterator interestiter = new InterestIterator(timeTable);
foreach (Study s in timeTable.getTimeTable())
{
Console.WriteLine(s.Name + " / " + s.Interest + " / " + s.LastTime);
}
Console.WriteLine("----------------------------------------------");
// 날짜 기준 정렬(시간이 가까운 순)
SortIterator dateiter = new DateIterator(timeTable);
foreach(Study s in timeTable.getTimeTable())
{
Console.WriteLine(s.Name + " / " + s.Interest + " / " + s.LastTime);
}
Console.WriteLine("----------------------------------------------");
}
}
}
- 실행 결과
<참고 사이트>