반복자 패턴 (이터레이터 패턴, iterator pattern)
반복자 패턴 (이터레이터 패턴, iterator pattern)
컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체안에 들어있는 모든 항목에 접근할 수 있게 해 주는 방법을 제공해 주는 패턴.
자바를 예로 들었을 때 List 와 Map 을 동일한 방법으로 접근 하는 거라고 생각하면 됨
필요한 이유
자바의 컬렉션(Collection) 은 Set, List, Queue, Map 이 있음 4가지 모두 데이터를 저장하는 방식이 다르다 보니 안에 있는 모든 데이터를 가져온다고 할 때 모두 다른 쿼리로 값을 가져와야함
List<Long> testList = new ArrayList<>();
testList.add(1L);
for (int i = 0; i < testList.size(); i++) {
System.out.println(testList.get(i));
}
HashMap<Long, Long> testMap = new HashMap<>();
testMap.put(1L, 1L);
for (Long key : testMap.keySet()) {
System.out.println(testMap.get(key));
}
이렇게 같은 값을 가져오는 거지만 로직이 달라짐
캡슐화 되지 않은 반복에 대해서 사용자가 각기 다른 구조들에 대해서 모두 파악하고 있어야 함
반복문을 돌아서 가져오는 게 동일하다고 하면 반복기능만 통일해서 사용할 수 있지 않을까 ? => 이터레이터 패턴
구조
- Iterator : 집합체의 요소들을 순서대로 검색하기 위한 인터페이스 정의
- ConcreateIterator : Iterator 인터페이스를 구현함
- Aggregate : 여러 요소들로 이루어져 있는 집합체
- ConcreateAggregate : Aggreagate 인터페이스를 구현하는 클래스
예제
public interface Snack {
void make();
}
public class Candy implements Snack {
@Override
public void make() {
System.out.printf("사탕 만들기");
}
}
public class Chocolate implements Snack {
@Override
public void make() {
System.out.printf("초콜릿 만들기");
}
}
public class CandyFactory {
public static final int MAX_CAPA = 100;
public static final List<Candy> CANDY_LIST = new ArrayList<>();
public void make() {
for(int i=0; i<MAX_CAPA; i++) {
CANDY_LIST.add(new Candy());
}
}
public Iterator<Candy> createIterator() {
return CANDY_LIST.stream().iterator();
}
}
public class ChocolateFactory {
public static final int MAX_CAPA = 100;
public static final HashMap<Integer, Chocolate> CHOCOLATE = new HashMap<>();
public void make() {
for(int i=0; i<MAX_CAPA; i++) {
CHOCOLATE.put(i, new Chocolate());
}
}
public Iterator<Chocolate> createIterator() {
return CHOCOLATE.values().iterator();
}
}
public class Main {
public static void main(String[] args) {
ChocolateFactory chocolateFactory = new ChocolateFactory();
CandyFactory candyFactory = new CandyFactory();
chocolateFactory.make();;
candyFactory.make();
Iterator<Candy> candyIterator = candyFactory.createIterator();
Iterator<Chocolate> chocolateIterator = chocolateFactory.createIterator();
while(candyIterator.hasNext()) {
Candy candy = candyIterator.next();
candy.make();
}
while(chocolateIterator.hasNext()) {
Chocolate chocolate = chocolateIterator.next();
chocolate.make();
}
}
}
=> CandyFactory 는 Candy 를 List 로 가지고 있고, ChocolateFactory 는 Chocolate 를 Map 으로 가지고 있지만 이터레이터 패턴을 사용했을 때에는 Main 에선 같은 반복문의 형태로 값을 가져오는 걸 알 수 있음
혹, 집합체에 이터레이터를 만드는 메소드가 제공되지 않는다면 직접 구현해주는 것도 방법임
Iterator 패턴 vs for 문
1) Iterator 패턴을 사용했을 때
while (it.hasNext()) {
Chocolate chocolate = (Chocolate) it.next();
System.out.println(chocolate.getNum());
}
2) for문을 사용했을 때
for (int i = 0; i < ChocolateFactory.getLength(); i++) {
System.out.println(ChocolateFactory.getChocolate(i).getNum());
}
- ChocolateFactory의 구현에 의존하지 않는다.
- ChocolateFactory에 무언가 수정사항이 생기더라도 hocolateFactory가 올바른 Iterator를 반환하기만 한다면 while문이 사용되는 Main은 수정할 필요가 없다. 하나의 클래스를 수정하더라도 다른 클래스에 큰 영향 없이 작은 수정 만으로도 끝낼 수 있음
Collection iterator
Collections.java (interface)
자바 모든 Collection 들은 해당 파일을 상속 받기 때문에 iterator() 메소드 사용 가능
HashMap.java
values() 같은 경우에도 Collection<?> 로 리턴해주기 때문에 iterator() 메소드 사용 가능