CS/디자인패턴

[디자인패턴] Iterator패턴 (이터레이터,반복자)

mabb 2023. 5. 24. 14:43
반응형

▶디자인 패턴 ( Design Pattern)
:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four)라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 
▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.
---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

 

▶Iterator 패턴

"hasNext()와 next()"
"순회기능을 따로 분리하기"

-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스


 

▶Iterator 패턴에 대한 이해

1. 컬렉션, 배열 등 내부에 여러 요소를  가지고 있는 자료구조(Data Structure)를 집합체(Aggregate)라고 표현한다.
2. 우리는 이러한 집합체 내부의 요소를 순차적으로 하나하나 꺼내어 보고 각각의 요소에 대해 작업을 하고는 한다.

3. 이러한 집합체는 데이터(data)를 효율적으로 저장하고, 꺼내고, 삭제하고 다루는 것에 집중해야 한다.
(단일 책임 원칙, SRP)

4. 집합체 내부에 저장된 요소를 처음부터 끝까지 하나하나 꺼내어 보는 것을 순회한다고 표현한다.

5. 순회하는 방법은 여러 가지가 있을 수 있다. 오름차순, 내림차순, 트리구조에서 DFS나 BFS 등...

6. 배열, 컬렉션 등 데이터를 효율적으로 저장하고, 꺼내고, 삭제하는 자료구조가 순회 알고리즘까지 갖는다면? 내부 응집도가 떨어진다. 그렇다면 각각의 컬렉션들마다 순회하는 코드를 다 따로따로 작성해주어야 하는가? 순회하는 방식이 바뀔 때마다 코드를 변경해주어야 하는가?

7. 보통 집합체를 순회하며 각 요소, 요소에  하나씩 접근하기 위해서는 for문이나 while 같은 루프문을 사용한다.
 예를 들어 for문을 이용하여 배열의 요소에 순차적으로 접근할 때는 다음과 같이 작성한다.
 for(int i = 0; i < arr.length; i++){ arr[i] ... } , for(int tmp : arr) { tmp ... } 

8. 순회하여야 할 집합체의 종류가 달라진다면?  순회하는 방식이 달라진다면? 트리 구조를 DFS, BFS 등으로 순회한다면? ArrayList를 오름차순으로 순회하다가 내림차순으로 순회한다면?
9. Iterator패턴에서는 '순회하는 방법'과 '집합체'를 분리한다.

10. Iterator패턴에서 순회하는 코드는 Iterator 인터페이스의 hasNext() 메서드와 next() 메서드로 틀을 잡고 구체적인 순회 방법의 구현은 Iterator의 구상 클래스에서 정한다. 이때 '집합체' 또는 '순회 방법'을 바꾼다고 해도 아래의 while 문은 변경을 할 필요가 없이 Iterator it; 에 대입되는 구상 반복자만 바꾸어주면 된다.
 while(haxNext()){ it.next() ...}

컬렉션 별 순회방법을 컬렉션에서 분리하여 구현하였고 구상이 아닌 추상에 의존하여 재사용성을 높였다.

 

▶왜 사용하는가

-배열, 리스트, 트리, 컬렉션 등의 자료구조를 집합체라고 칭할 때, 이 집합체 안의 요소들을 하나하나 순회하며 작업을 할 때 사용한다. 만약 추상이 아닌 구상에 의존하여 각각의 집합체, 순회 방법에 대한 코딩을 한다면 재사용성이 떨어지고 유지보수가 힘들어질 것이다.  집합체 내의 요소를 순회하는 '반복자'를 집합체(컬렉션, 배열 등)와 분리하여 재사용성을 높이는 구조를 만드는 것이 바로 Iterator패턴이다.
Java API에서도 바로 이 Iterator 패턴을 활용하여 라이브러리를 구현해 놓았다.
그리고 여러 가지 자료구조를 기반으로 구현된 자바의 컬렉션프레임워크(CollectionFramework)에서
Iterator와 Iterable 인터페이스를 구현하고 있다.  
ArrayList 도 iteratable을 구현하고 있어 iterator 순회가 가능하다.

Iterator (Java SE 20 & JDK 20)

Type Parameters: E - the type of elements returned by this iterator All Known Subinterfaces: EventIterator, ListIterator , PrimitiveIterator , PrimitiveIterator.OfDouble, PrimitiveIterator.OfInt, PrimitiveIterator.OfLong, XMLEventReader All Known Implement

docs.oracle.com

 

Iterable (Java SE 20 & JDK 20)

Type Parameters: T - the type of elements returned by the iterator All Known Subinterfaces: BeanContext, BeanContextServices, BlockingDeque , BlockingQueue , Collection , Deque , DirectoryStream , EventSet, List , NavigableSet , NodeSetData , Path, Queue ,

docs.oracle.com

 

 

▶클래스다이어그램

Iterator패턴의 클래스 다이어그램

 

▶각 클래스(인터페이스)의 역할

  ▶Iterator 인터페이스 - 아래의 코드에서 interface Iterator
     : hasNext() 와 next() 추상메소드 선언
  ▶Iterator 구상 클래스 - 아래의 코드에서 class concreteIterator
     : hasNext() 와 next()를 오버라이드하여 구체적인 순회 방법을 정의
  Iterable 인터페이스 - 아래의 코드에서 inerface Aggregate
     : iterator() 추상메서드를 선언
   Iterable 구상 클래스 - 아래의 코드에서 class concreteAggregate
     :  iterator() 메서드를 재정의 하여 해당 집합체에 해당하는 구상 반복자를 만들어낸다. 해당 집합체가 가져야 할 기능(메서드)도 구현한다.

  ▶ 클라이언트 - 아래의 코드에서 main메서드
     :집합체의 요소를 순회하며 사용하는 부분으로 아래의 예제에서는 main메서드이다.
       Iterator 구현 객체만 바꿔주면 되어서 변경을 최소화할 수 있다.

 

 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)
 
Iterator.java

package main.java.designpattern.iterator;

public interface Iterator<E> {

    public abstract boolean hasNext();
    public abstract <E> E next();

}

 
Aggregate.java

package main.java.designpattern.iterator;

public interface Aggregate<E> {
    public abstract Iterator<E> iterator();
}

 
ConcreteIterator.java

package main.java.designpattern.iterator;

public class ConcreteIterator<E> implements Iterator<E> {
    private ConcreteAggregate ConcreteAggregate;
    private int index;

    public ConcreteIterator(ConcreteAggregate ConcreteAggregate){
        this.ConcreteAggregate = ConcreteAggregate;
        this.index = 0;
    }

    @Override
    public boolean hasNext(){
        if(index < ConcreteAggregate.getLength()){
            return true;
        }else{
            return false;
        }
    }

    @Override
    public  <E> E next(){
        E e = (E) ConcreteAggregate.getItemAt(index);
        index++;
        return (E)e;
    }
}

 
ConcreteAggregate.java

package main.java.designpattern.iterator;

import java.util.ArrayList;

public class ConcreteAggregate<E> implements Aggregate<E> {
//    private Item[] items;
    private ArrayList<E> e;
    private int last = 0;

//    public ConcreteAggregate(int maxSize){
//        this.items = new Item[maxSize];
//    }

    public ConcreteAggregate(int maxSize){
        this.e = new ArrayList<E>(maxSize);
    }

    @Override
    public Iterator iterator(){
        return new ConcreteIterator(this);
    }


//    public Item getItemAt(int index){
//        return items[index];
//    }

    public E getItemAt(int index){
        return e.get(index);
    }

//    public void appendItem(Item item){
//        this.items[last] = item;
//        last ++;
//    }

    public void appendItem(E e){
        this.e.add(e);
        last ++;
    }

    public int getLength(){
        return last;
    }

}

 
 
Main.java

package main.java.designpattern.iterator;

public class Main {

    public static void main(String[] args){
        ConcreteAggregate ConcreteAggregate = new ConcreteAggregate(3);
        ConcreteAggregate.appendItem(new Item("1번 아이템"));
        ConcreteAggregate.appendItem(new Item("2번 아이템"));
        ConcreteAggregate.appendItem(new Item("3번 아이템"));
        ConcreteAggregate.appendItem(new Item("4번 아이템"));
        ConcreteAggregate.appendItem(new Item("5번 아이템"));

        Iterator<Item> it = ConcreteAggregate.iterator();

        while(it.hasNext()){
            Item item = it.next();
            System.out.println("요소에 대한 처리, item이름 출력  :  " + item.getItemName());
        }
    }
}

 
---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형