본문 바로가기

디자인 패턴/디자인 패턴

[디자인 패턴] Iterator 패턴

책을 공부하며 새로운 문제를 만들고 구현한것을 기록한 게시글입니다.

(이해한 내용을 바탕으로 작성했기 때문에, 내용이 정확하지않고 틀린부분이 있을수있으며, 예시가 패턴을 사용하기에 알맞지 않을수도있습니다)


Iterator 패턴

자료구조의 구조를 드러내지않으면서, 해당 구조에있는 모든 항목을 참조할수있도록함.

 

클라이언트에게 자료구조를 드러내지않으면서, 자료구조에 값을 참조할수있도록 한다.

클라이언트는 자료구조에 대해 몰라도 되기때문에, 이후에 자료구조가 추가되거나, 변경되더라도 클라이언트 코드는 변경될필요가 없다.


 

Iterator패턴을 구현해봤다.

각각 다른 자료구조를 사용하는 클래스가있다. 예를들어,

Array를 구현한 RepeatOnArray가 있고, ArrayList를 구현한 RepeatOnArrayList가 있다.

위 두 클래스가 갖고있는 자료구조에서 항목을 가져와야하는 SuperRepeat클래스가 있다. 이때, OCP원칙을 지키며 구현해보자.

 

핵심

항목참조를 담당하는 interface를 만들고, 이 interface를 구현한 클래스들은 모두 항목참조가 가능하도록한다. 이때, 다형성을 이용하기때문에, 클라이언트는 각각의 클래스의 자료구조에대해 몰라도된다.


구현

우선, Array를 갖고있는 RepeatOnArray와 ArrayList를 자료구조로 갖고있는 RepeatOnArrayList를만들어보자.

class RepeatOnArrayList implements RepeatAble{
    private ArrayList<Integer> arrList;
    
    public RepeatOnArrayList(){ 
        arrList = new ArrayList<Integer>();
        for(int i = 0; i < 15; i++) arrList.add(i); // 편의를 위해 0~14까지의 값을 넣었다.
    }
    
    public Repeat getRepeat(){ // 반복자를 생성하는 메소드다 RepeatAble을 구현한 클래스는 모두 반복자를 호출한다.
        return new ArrayListRepeater(this.arrList);
    }
    
}
class RepeatOnArray implements RepeatAble{
    private int[] arr;
    
    public RepeatOnArray(){
        arr = new int[15];
        for(int i = 0; i < arr.length; i++) arr[i] = i; // 편의를 위해 0~14까지의 값을 넣었다.
    }
    
    public Repeat getRepeat(){ // 반복자를 생성하는 메소드다 RepeatAble을 구현한 클래스는 모두 반복자를 호출한다.
        return new ArrayRepeater(this.arr);
    }
    
}

RepeatAble 인터페이스에 대해서는 당장 신경쓰지 않아도 된다.

지금은 RepeatOnArray클래스의 자료구조를 반복시키는 Repeat인터페이스를 반환하는 메소드를 정의한 인터페이스 라고 생각하자.

 

RepeatOnArray와 RepeatOnArrayList는 각각 Array와 ArrayList를 자료구조로 갖고있다. 해당 자료구조를 반복시키기 위해서, 항목에 대한 참조를 담당하는 인터페이스 Repeat를 만들자.

 

interface Repeat{
    boolean hasNext();
    void next();
    void print();
}

이제 RepeatOnArray와 RepeatOnArrayList에 반복을 담당하는 ArrayRepeater, ArrayListRepeater를 생성하자. 위 두 클래스는 Repeat를 구현할것이며 이제, RepeatOnArray와 RepeatOnArrayList에 대한 반복(항목에 대한 참조)는 모두 ArrayRepeater, ArrayListRepeater가 담당할것이다.

class ArrayListRepeater implements Repeat{
    private ArrayList<Integer> arrList; 
    private int index;
    public ArrayListRepeater(ArrayList<Integer> arrList){ // RepeatOnArray에 있는 arrList 인스턴스를 참조한다.
        this.arrList = arrList;
        index = -1; 
        // 첫번째 위치부터 참조하기위해 index 초기값을 -1로 잡았다. 
        //이것은 구현에 따라 달라질수있다. 
        //이후에 나올 SuperRepeat클래스를 보면 왜 index를 -1로 했는지 이해가 될것이다.
    }
    
    public boolean hasNext(){ // 다음위치에 값이 있는지 참조한다.
        if(index + 1 < arrList.size()) { // index+1한 값이 arrList.size()보다 작다면 값이 있다고 가정한다.
            next();
            return true;
        }
        return false;
    }
    
    public void next(){
        index++;
    }
    
    public void print(){
        if(index < 0) next(); // 만약 index가 -1인상태로 호출된다면 오류가 남으로 index < 0 이라면 next를 호출해 양수로 바꾼다.
        System.out.print(this.arrList.get(index) + " ");
    }
    
}

글이 길어질거같아서, ArrayListRepeater만 첨부한다. ArrayRepeater는 위와 비슷하다

hasNext()는 값이 있는지 참조한다. 각 자료구조별로, hasNext()와 print()의 구현은 다를것이다. 

하지만, 이후에 참조를 담당할 SuperRepeat에서는 자료구조에 따라, 구현방식이 변하지않는다.

즉, 이렇게 구현함으로써 클래스의 변경혹은 생성이 연관된 클래스(여기서는 반복을 주관하는 SuperReapeat일것이다.)의 변경을 유발하지않는다는것이다.

예를들어, ArrayList와 Array말고 String자료구조가 새로 추가되었다고 하자. 우리는 String자료구조의 각 글자에 접근하기위해 String.charAt(int) 메소드를 호출하게된다. 위와같이 이터레이터 패턴을 사용하지않는다면, 반복을 담당할 SuperRepeat는 String에 맞춰 반복 메소드를 추가해야한다. 하지만, 이터레이터 패턴을 사용하면, String자료구조가 있는 클래스에 Repeat를 구현하도록 함으로써, SuperRepeat의 변경을 방지할수있다. 

 

이제, SuperRepeat의 코드를 보자.

 

class SuperRepeat<A extends RepeatAble>{ // 여기서 사용한 제네릭은 큰 의미가 없다. C언어의 typedef와 같은 목적으로 사용되었다고 생각하자.
    
    private A repeatTarget;
    private Repeat repeater;
    public SuperRepeat(){};
    
    public void set(A repeatTarget){
        System.out.println(repeatTarget.getClass().getName()); // 입력받은 반복할대상의 클래스 이름을 출력한다.
        this.repeater = repeatTarget.getRepeat();
    }
    
    public void repeat(){
        while(repeater.hasNext()) { // 반복대상은 모두 Repeat을 구현한다.
            repeater.print(); // 따라서, hasNext()로 다음 위치에 값이 있는지 확인, print로 자신을 출력할수있다.
        }
    }
    
}

SuperRepeat는 각 자료구조별로 반복을 호출할 필요가없다. 반복할 자료구조는 모두 Repeat을 구현하므로, repeat()와 hasNext를 호출하기만 하면된다.

 

메인 함수는 다음과 같다. 

    public static void main(String[] args){
        SuperRepeat Repeater = new SuperRepeat();
        Repeater.set(new RepeatOnArrayList());
        Repeater.repeat();
        Repeater.set(new RepeatOnArray());
        Repeater.repeat();
    }

SuperRepeat에 RepeatAble을 구현하는 클래스를 넣고, repeat를 호출하면, 어떤 자료구조가 들어가더라도,(RepeatAble과 Repeat를 구현한다면) 반복시킬수있다.

 

이제, 새로운 자료구조가 추가되거나,  기존 자료구조에 변경이 있더라도 SuperRepeat의 코드는 변경될필요가없다.

 


전체코드

https://github.com/devxb/DesignPatterns/tree/main/DesignPatterns/prob8_Iterator

 

devxb/DesignPatterns

디자인 패턴 🤔. Contribute to devxb/DesignPatterns development by creating an account on GitHub.

github.com