이터레이터 패턴 Iterator Pattern

Bam·2022년 3월 5일
0

Design Pattern

목록 보기
10/13
post-thumbnail
post-custom-banner

이터레이터 패턴

이터레이터 패턴의 소개는 다음과 같이 합니다. 컬렉션의 구현 방법을 외부에 노출시키지 않으면서, 그 컬렉션 내부의 모든 항목에 대하여 접근할 수 있도록 하는 패턴이다. 조금 더 쉬운 말로 풀어보자면, 동작을 어떻게 하는지는 몰라도 내부의 항목에 대해서 반복 작업이 가능하다라고 할 수도 있습니다. 이터레이터 패턴은 행동 패턴에 포함됩니다.

가장 좋은예는 어떤 코드에 배열과 리스트가 혼합되어 있습니다. 둘 다 인덱스를 이용하고, 여러개의 데이터를 저장한다는 공통점이 있는데 접근방법은 조금 다릅니다. 이때 이터레이터 패턴을 이용하면 동일한 인터페이스를 이용해서 배열이든 리스트이던 모든 데이터에 접근할 수 있게 됩니다.

이터레이터 패턴 구현

두 학교가 있습니다. 학생 수의 감소로 두 학교를 통합하려고 하는데, 두 학교의 시스템에서 사용하는 방식이 다릅니다. 한 학교는 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();
    }

이처럼 기존의 제각각인 호출법대신 통일된 인터페이스를 통해 컬렉션의 요소에 접근할 수 있는 이터레이터 패턴이었습니다.

post-custom-banner

0개의 댓글