이터레이터 패턴
의 소개는 다음과 같이 합니다. 컬렉션의 구현 방법을 외부에 노출시키지 않으면서, 그 컬렉션 내부의 모든 항목에 대하여 접근할 수 있도록 하는 패턴이다. 조금 더 쉬운 말로 풀어보자면, 동작을 어떻게 하는지는 몰라도 내부의 항목에 대해서 반복 작업이 가능하다라고 할 수도 있습니다. 이터레이터 패턴
은 행동 패턴에 포함됩니다.
가장 좋은예는 어떤 코드에 배열과 리스트가 혼합되어 있습니다. 둘 다 인덱스를 이용하고, 여러개의 데이터를 저장한다는 공통점이 있는데 접근방법은 조금 다릅니다. 이때 이터레이터 패턴
을 이용하면 동일한 인터페이스를 이용해서 배열이든 리스트이던 모든 데이터에 접근할 수 있게 됩니다.
두 학교가 있습니다. 학생 수의 감소로 두 학교를 통합하려고 하는데, 두 학교의 시스템에서 사용하는 방식이 다릅니다. 한 학교는 ArrayList를 이용하고, 한 학교는 배열을 이용합니다.
//ArraList를 사용하는 AL학교 클래스
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;
}
}
//배열을 사용하는 Arr학교 클래스
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(); //다음 객체 반환
}
이제 이터레이터 인터페이스를 적용해서 각 객체의 이터레이터 객체를 만들어줍니다
//이터레이터가 적용된 AL학교의 이터레이터 객체
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;
}
}
//이터레이터가 적용된 Arr학교의 이터레이터 객체
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();
}
이처럼 기존의 제각각인 호출법대신 통일된 인터페이스를 통해 컬렉션의 요소에 접근할 수 있는 이터레이터 패턴이었습니다.