이터레이터(iterator)는 반복자라는 의미를 가지고 있습니다. 그 이름처럼 반복에 대한 문제를 해결해주는 디자인 패턴입니다.
1. 이터레이터 패턴
이터레이터 패턴의 소개는 다음과 같이 합니다. 컬렉션의 구현 방법을 외부에 노출시키지 않으면서, 그 컬렉션 내부의 모든 항목에 대하여 접근할 수 있도록 하는 패턴이다. 조금 더 쉬운 말로 풀어보자면, 동작을 어떻게 하는지는 몰라도 내부의 항목에 대해서 반복 작업이 가능하다라고 할 수도 있습니다.
가장 좋은예는 어떤 코드에 배열과 리스트가 혼합되어 있습니다. 둘 다 인덱스를 이용하고, 여러개의 데이터를 저장한다는 공통점이 있는데 접근방법은 조금 다릅니다. 이때 이터레이터 패턴을 이용하면 동일한 인터페이스를 이용해서 배열이든 리스트이던 모든 데이터에 접근할 수 있게 됩니다.
2. 이터레이터 패턴 구현
두 학교가 있습니다. 학생 수의 감소로 두 학교를 통합하려고 하는데, 두 학교의 시스템에서 사용하는 방식이 다릅니다. 한 학교는 ArrayList를 이용하고, 한 학교는 배열을 이용합니다.
public class ALSchool {
ArrayList studentInfos;
public ALSchool() {
studentInfos = new ArrayList();
addStudentInfo("사과al", 10);
addStudentInfo("바나나al", 11);
addStudentInfo("포도al", 12);
}
public void addStudentInfo(String name, int studentID) {
StudentInfo studentInfo = new StudentInfo(name, studentID);
studentInfos.add(studentInfo);
}
public ArrayList getStudentInfos() {
return studentInfos;
}
}
public class ArrSchool {
StudentInfo[] studentInfos;
int index = 0;
public ArrSchool() {
studentInfos = new StudentInfo[3];
addStudentInfo("복숭아arr", 00);
addStudentInfo("망고arr", 01);
addStudentInfo("토마토arr", 02);
}
public void addStudentInfo(String name, int studentID) {
StudentInfo studentInfo = new StudentInfo(name, studentID);
studentInfos[index] = studentInfo;
index++;
}
public StudentInfo[] getStudentInfos() {
return studentInfos;
}
}
이제 학생의 정보를 출력하는 View를 만들려고 하는데 다음과 같은 문제가 발생합니다. 두 학교의 시스템이 다른 나머지 접근 방식이 달라서 호출하는 방법이 달라지게 됩니다. 그래서 같은 반복문을 두 번 사용하게되는 아래와 같은 상황이 발생하게 됩니다. 그래서 우리는 이 불편함을 해결하고자 반복 구조를 캡슐화 하여 이터레이터 패턴을 만들려고 합니다.
public class ViewSystem {
ALSchool alSchool = new ALSchool();
ArrayList alSchoolStudentInfos = alSchool.getStudentInfos();
ArrSchool arrSchool = new ArrSchool();
StudentInfo[] arrSchoolStudentInfos = arrSchool.getStudentInfos();
public void print() {
System.out.println("==alSchool의 학생들");
for (int i = 0; i < alSchoolStudentInfos.size(); i++) {
StudentInfo studentInfo = (StudentInfo) alSchoolStudentInfos.get(i);
System.out.println(studentInfo.getName() + ", " + studentInfo.getStudentID());
}
System.out.println("==arrSchool의 학생들");
for (int i = 0; i < arrSchoolStudentInfos.length; i++) {
StudentInfo studentInfo = arrSchoolStudentInfos[i];
System.out.println(studentInfo.getName() + ", " + studentInfo.getStudentID());
}
}
}
이터레이터 패턴을 위해 이터레이터 인터페이스를 우선 정의합니다. 인터페이스 내용은 간단합니다. 다음 요소가 있는지 확인하고, 다음 객체를 반환합니다. 이 구조를 보니까 우리가 예를든 배열과 리스트 뿐만 아니라, 해시나 다른 객체 컬렉션들에도 적용할 수 있다는 것을 느낄 수 있습니다.
public interface Iterator {
boolean hasNext(); //다음 요소가 있는지 확인
StudentInfo next(); //다음 객체 반환
}
이제 이터레이터 인터페이스를 적용해서 각 객체의 이터레이터 객체를 만들어줍니다.
public class ALSchoolIterator implements Iterator{
ArrayList<StudentInfo> elems;
int position = 0;
public ALSchoolIterator(ArrayList<StudentInfo> elems) {
this.elems = elems;
}
@Override
public boolean hasNext() {
if(position >= elems.size()){
return false;
}
else {
return true;
}
}
@Override
public StudentInfo next() {
StudentInfo elem = elems.get(position);
position += 1;
return elem;
}
}
public class ArrSchoolIterator implements Iterator {
StudentInfo[] elems;
int position = 0;
public ArrSchoolIterator(StudentInfo[] elems) {
this.elems = elems;
}
@Override
public boolean hasNext() {
if (position >= elems.length || elems[position] == null) {
return false;
}
else {
return true;
}
}
@Override
public StudentInfo next() {
StudentInfo studentInfo = elems[position];
position += 1;
return studentInfo;
}
}
이터레이터 객체를 만든 후 사용하기 위해서 기존 객체를 수정합니다.
public class ArrSchool {
StudentInfo[] studentInfos;
int index = 0;
public ArrSchool() {
studentInfos = new StudentInfo[3];
addStudentInfo("복숭아arr", 00);
addStudentInfo("망고arr", 01);
addStudentInfo("토마토arr", 02);
}
public void addStudentInfo(String name, int studentID) {
StudentInfo studentInfo = new StudentInfo(name, studentID);
studentInfos[index] = studentInfo;
index++;
}
//이 부분이 추가!
public Iterator createIterater() {
return new ArrSchoolIterator(studentInfos);
}
}
이제 직접 사용하기 위해 ViewSystem을 꾸밀건데, 기존의 시스템과 차이를 비교해보면 확연히 짧아지고 간단해진 코드를 볼 수 있습니다.
public class ViewSystem {
ALSchool alSchool;
ArrSchool arrSchool;
public ViewSystem(ALSchool alSchool, ArrSchool arrSchool) {
this.alSchool = alSchool;
this.arrSchool = arrSchool;
}
public void print(){
Iterator alSchoolIterator = alSchool.createIterater();
Iterator arrSchoolIterator = arrSchool.createIterater();
System.out.println("--AlsSchool 학생 명단--");
print(alSchoolIterator);
System.out.println();
System.out.println("--ArrSchool 학생 명단 --");
print(arrSchoolIterator);
}
private void print(Iterator iterator){
while(iterator.hasNext()) {
StudentInfo studentInfo = (StudentInfo)iterator.next();
System.out.print(studentInfo.getName()+": "+studentInfo.getStudentID()+"\n");
}
}
}
public static void main(String[] args) {
ALSchool alSchool = new ALSchool();
ArrSchool arrSchool = new ArrSchool();
ViewSystem viewSystem = new ViewSystem(alSchool, arrSchool);
viewSystem.print();
}
이처럼 기존의 제각각인 호출법대신 통일된 인터페이스를 통해 컬렉션의 요소에 접근할 수 있는 이터레이터 패턴이었습니다.
https://github.com/Bam-j/DesignPattern/tree/master/src/Iterator
'Programming > 디자인 패턴' 카테고리의 다른 글
프록시 패턴 Proxy Pattern (0) | 2021.10.02 |
---|---|
스테이트 패턴 State Pattern (0) | 2021.09.29 |
템플릿 메소드 패턴 Template Method Pattern (0) | 2021.09.23 |
퍼사드 패턴 Façade Pattern (0) | 2021.09.20 |
어댑터 패턴 Adapter 패턴 (0) | 2021.09.19 |
댓글