반복자 패턴(Iterator Pattern) ; SRP
반복자 패턴은 컬렉션의 구현 방법을 노출시키지 않으면서도
그 집합체 안에 들어 있는 모든 항목에 접근할 수 있게 해 주는 방법을 제공해줍니다.
단일 책임의 원칙(SRP : Single Responsibility Principle)
클래스를 변경하는 이유가 단 한가지어야 한다.
- 컬렉션의 기능과 반복하는 책임을 분리한다.
- 따라서 컬렉션의 기능을 변경한다면, 컬렉션 클래스를 변경하고
- 반복하는 기능에 대한 변경을 한다면 반복자 클래스를 변경한다
책에서 나오는 예제 - java.util.Iterator 활용
Java Collection 을 크게 나누면 3가지로 나눌 수 있다.
List
순서가 있는 집합체, 중복 허용
1 | public interface List<E> extends Collection<E> { |
Set
중복을 허용하지 않는 집합
1 | public interface Set<E> extends Collection<E> { |
Map
<Key, Value> 로 구성되는 집합
- Key 의 타입은 Set 이다.
- Value 의 타입은 Collection 이다.
- 따라서 Value 로 Map 이 다시 등장할 수도 있다.
1 | public interface Map<K,V> { |
1 | public interface Collection<E> extends Iterable<E> { |
Collection in iterator 결론
컬렉션을 사용한다면 모든 요소에 편하게 접근하는 것을 기대하고 사용하는 경우가 대부분이다.
따라서, 각 구성요소에 반복하며 접근하는 책임을 Iterator 라는 별도 인터페이스로 분리하고,
Collection 의 각 구현체에서 각 자료구조에 따라 반복하는 것에 대한 구현도 담당해야 한다는 것이다.
Spring 속 Iterator Pattern
org.springframework.util.CompositeIterator
는 JavaSE의 Iterator 인터페이스의 구현체이다.
Iterator set 을 멤버로 갖고 있으며, 모든 iterator 가 반복 작업을 완료할 때 까지 순차적으로 호출되는 여러 iterator 를 유지 / 관리 한다.
특히, remove 는 다중 스레드 환경에서 심각한 오류를 야기시킬 수 있기 때문에 UnsupportedOperationException
를 발생시키는 것이 인상적이다.
컴포지트 패턴으로도 볼 수 있어서 단순히 Iterator Pattern 이라고는 볼 수 없다. 하지만, 중요한 것은 반복에 대한 책임을 가지고 있는 객체라는 점이다.
1 | package org.springframework.util; |
생각해보자
Java Collection 을 수 없이 사용하면서, Iterator 라는 인터페이스에 대해 생각해 볼 겨를이 없었던 것 같다.
이게 별도 패턴으로 분류되어야 하는가 싶기는 하면서도, 단일 책임의 원칙이라는 관점에서 다시 생각해보면
Iterator 라는 매우 단순하지만 명확한 책임을 가지고 있는 객체도 별로 없는 것 같다는 생각이 든다.
다형성 관점에서의 Iterator
모든 Collection 은 동일한 Iterator 객체를 반환해준다. 따라서 어떤 Collection 을 사용하는 지에 상관없이(List, Set, Map)
‘모든 구성요소에 접근하고 싶다’ 는 목적이면 Iterator 를 반환해서 사용하면 된다.
주의(멀티스레드 환경)
iterator 의 remove()
는 신중하게 사용해야 한다. 멀티 스레드 환경에서 동일한 collection 에 대해 remove 는 안전성을 보장하지 않는다.
Java 의 stream 을 통한 반복에서 멀티스레드 안전성에 대해 한번 더 공부해봐야겠다는 생각이 들었다.