이터레이터 패턴 (Iterator Pattern) 이란?
- 컬렉션의 요소를 순회하는 패턴이다.
- 내부의 표현을 노출시키지 않는다.
List
,Stack
,Tree
,Set
과 같이 어떠한 컬렉션인지 노출시키지 않는다.
- 내부 표현을 노출시키면 컬렉션의 종류가 바뀔 때마다 클라이언트 코드를 변경해주어야 하는 불편함이 생긴다.
다이어그램으로 살펴보기
예시: 게시판 게시글 순회
Before: 인덱스를 직접 이용해 순회하기
- 전통적인 배열 및 컬렉션 순회 방법이다.
- 단, 컬렉션의 종류가 바뀌게 되면 클라이언트 코드를 수정해주어야 한다는 단점이 있다.
public class Client {
public static void main(String[] args) {
Board board = new Board();
board.addPost("디자인 패턴 개요");
board.addPost("알고리즘 개요");
board.addPost("데이터베이스 개요");
// 들어간 순서대로 순회하기
List<Post> posts = board.getPosts();
for (int i = 0 ; i < posts.size() ; i++) {
Post post = posts.get(i);
System.out.println(post.getTitle());
}
// 가장 최신 글 먼저 순회하기
Collections.sort(posts, (p1, p2) -> p2.getCreatedDateTime().compareTo(p1.getCreatedDateTime()));
for (int i = 0 ; i < posts.size() ; i++) {
Post post = posts.get(i);
System.out.println(post.getTitle());
}
}
}
After: Iterator
객체를 이용해 순회하기
- 클래스에서
Iterator
객체를 만들어주는 부분을 추가했다.getXxxIterator()
메서드명으로 추가했다.
public class Board {
List<Post> posts = new ArrayList<>();
public void addPost(String content) {
this.posts.add(new Post(content));
}
public void addPost(String content, LocalDateTime time) {
this.posts.add(new Post(content, time));
}
public Iterator<Post> getIterator() { return posts.iterator(); }
public Iterator<Post> getRecentPostIterator() {
return new RecentPostIterator(this.posts);
}
}
RecentPostIterator
는List
를createdDateTime
을 기준으로 포스트를 정렬한 뒤List
의iterator()
를 이용해Iterator
인터페이스를 구현하고 있다.
public class RecentPostIterator implements Iterator<Post> {
private Iterator<Post> internalIterator;
public RecentPostIterator(List<Post> posts) {
Collections.sort(posts, (p1, p2) -> p2.getCreatedDateTime().compareTo(p1.getCreatedDateTime()));
this.internalIterator = posts.iterator();
}
@Override
public boolean hasNext() {
return this.internalIterator.hasNext();
}
@Override
public Post next() {
return this.internalIterator.next();
}
}
Iterator
를 적용해서 이제 인덱스를 직접 지정할 필요가 사라졌다.- 컬렉션의 종류가
Map
,Set
등으로 바뀌어도iterator
만 적절하게 반환해주면 클라이언트 코드를 수정할 필요는 없다.
public class Client {
public static void main(String[] args) {
Board board = new Board();
board.addPost("디자인 패턴 개요", LocalDateTime.of(2021, 1, 1, 1, 1));
board.addPost("알고리즘 개요", LocalDateTime.of(2023, 1, 1, 1, 1));
board.addPost("데이터베이스 개요", LocalDateTime.of(2022, 1, 1, 1, 1));
// 들어간 순서대로 순회하기
Iterator<Post> iterator = board.getIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getTitle());
}
// 가장 최신 글 먼저 순회하기
Iterator<Post> recentPostIterator = board.getRecentPostIterator();
while(recentPostIterator.hasNext()) {
System.out.println(recentPostIterator.next().getTitle());
}
}
}
장점
관심사 분리
- 컬렉션 객체에서 순회 로직만 따로 분리하므로 순회 방식이 바뀌더라도 컬렉션 객체에 영향을 주지 않는다.
- 순회 알고리즘을 쉽게 수정할 수 있다.
유연성
- 동일한 컬렉션 객체에도 여러개의 반복자를 만들 수 있다.
- 각 반복자마다 다른 순회 알고리즘을 적용하여 여러 방식의 순회를 유연하게 적용할 수 있다.
캡슐화
- 캡슐화가 이루어져 클라이언트 코드에 영향을 주지 않고 컬렉션의 구현을 쉽게 바꿀 수 있다.
- 클라이언트는 반복자만 정상적으로 가져올 수 있으면 된다.
추상화
- 클라이언트에게 순회 세부사항을 숨김으로써 추상화가 되고 코드의 유지 관리가 쉬워지며 오류가 덜 발생한다.
단점
성능 오버헤드
- 객체 하나가 추가되는 오버헤드가 발생하는 것이므로 성능적으로는 손해일수 있다.
복잡성 증가
- 동일한 객체에 여러 반복자를 사용하는 경우 컬렉션이 더 복잡해질 수 있다.
제한된 액세스
- 읽기 전용 액세스를 제공하므로 애플리케이션의 기능이 제한될 수 있다.
불일치
- 컬렉션을 순회하는 도중 컬렉션이 변경되면 일관성 없는 동작을 초래할 수도 있다.
자바에서 사용되는 Iterator 패턴
자바의 Iterator
인터페이스
- 자바가 기본으로 제공하는
Iterator
인터페이스가 그 예이다.
과거엔
Enumeration
이라는 것도 있었는데, 현재는Iterator
로 대체되었다.
자바의 StAX 라이브러리
- Java StAX (Streaming API for XML) 의 Iterator 기반 API
XmlEventReader
,XmlEventWriter
- 비슷하게 생긴 SAX 라이브러리랑은 다르다.
- 콘솔 기반의 API 와 Iterator 기반의 API 를 제공한다.
- 아래의 코드는 Iterator 기반의 API 예이다.
- 콘솔 기반의 API 는 반복 기준이 의미 단위가 아니라 커서로 한줄한줄씩 읽어들이는 개념과 비슷하다고 볼 수 있을 것 같다.
- 콘솔 기반의 API 가 성능면에선 조금 더 효율적이다.
- 코드의 품질을 위해서는 Iterator 기반이 더 효율적이다.
public static void main(String[] args) throws FileNotFoundException, XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(new FileInputStream("Book.xml"));
while (reader.hasNext()) {
XMLEvent nextEvent = reader.nextEvent();
if (nextEvent.isStartElement()) {
StartElement startElement = nextEvent.asStartElement();
QName name = startElement.getName();
if (name.getLocalPart().equals("book")) {
Attribute title = startElement.getAttributeByName(new QName("title"));
System.out.println(title.getValue());
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book title="오징어 게임"/>
<book title="숨바꼭질"/>
<book title="우리집에 왜 왔니"/>
</books>
스프링의 CompositeIterator
- 여러
Iterator
를 조합하여 사용하는 경우에 사용할 수 있다. - 많이 사용되는 API 는 아니다.
레퍼런스
반응형
'Java > 자바 디자인 패턴' 카테고리의 다른 글
중재자 패턴 (Mediator Pattern) 이란? (0) | 2023.05.09 |
---|---|
인터프리터 패턴 (Interpreter Pattern) 이란? (0) | 2023.04.30 |
커맨드 패턴 (Command Pattern) 이란? (0) | 2023.04.28 |
책임 연쇄 패턴 (Chain Of Responsibility) 이란? (2) | 2023.04.26 |
프록시 패턴 (Proxy Pattern) 이란? (0) | 2023.04.25 |