---
# System prepended metadata

title: Iterator Pattern

---

# Iterator Pattern


https://refactoring.guru/design-patterns/iterator


```go
userCollection := &UserCollection{
    users: []*user{user1, user2},
}

iter := userCollection.createIterator()
for iter.hasNext() {
    user := iter.getNext()
    fmt.Printf("User is %+v\n", user)
}
```


## 왜 쓸까?

복잡한 구조를 가진 collection를 클라이언트로부터 복잡성을 숨길 수 있다
-> 편리성과 보안성이 증가한다
-> 순환하는 코드를 application 전반에서 줄일 수 있고, collection 을 직접 접근하면서 발생하는 부주의하거나 악의적인 액션을 방지한다

순회 로직을 비즈니스 로직안에 넣는다면 코드의 책임이 모호해지고 유지보수가 어려워질 수 있다
-> 단순하지 않은 순회 알고리즘을 가진 데이터 구조가 있다면 매번 interate 할 때 마다 긴 코드를 작성해야 한다
-> 순회로직을 숨겨서 관심사를 분리할 수 있도록 한다

collection 의 type을 미리 알 수 없는 경우(혹은 알리고 싶지 않은 경우) iterator 를 사용한다
-> general 한 interface 를 제공해서 다양한 종류의 collection 이라도 iterator 만 전달하면 잘 동작한다.
-> Concreate Iterator 의 자료구조나 구현이 바뀐다고 하더라도 클라이언트 코드의 변화가 없다

## 구현


### Iterator

```go
type iterator interface {
	hasNext() bool
	getNext() *user
}
```

element 를 순서대로 순회할 수 있는 인터페이스
다음 element가 존재하는지를 얻기위한 `hasNext()`와 다음 요소를 얻기 위한 `getNext()` 가 있다.
편의를 위해 이전 element 가져오기, 현재 위치 추적, 반복 종료 확인, element 삭제 등 몇 가지 메서드를 추가할 수 있다.

### ConcreateIterator
```go
type userIterator struct {
	index int
	users []*user
}

func (u *userIterator) hasNext() bool {
	if u.index < len(u.users) {
		return true
	}
	return false

}

func (u *userIterator) getNext() *user {
	if u.hasNext() {
		user := u.users[u.index]
		u.index++
		return user
	}
	return nil
}
```

Iterator 인터페이스를 실제로 구현한다.
순회하기 위해 필요한 정보를 가지고 있어야 한다.

### Aggregate
```go
type collection interface {
	createIterator() iterator
}
```

Iterator 를 만들어내는 인터페이스
이 인터페이스는 iterator 를 만들어 내는 메소드를 가지고 있다.

### ConcreateAggregate
```go
type UserCollection struct {
	users []*user
}

func (u *UserCollection) createIterator() iterator {
	return &userIterator{
		users: u.users,
	}
}
```

Aggregate 인터페이스를 실제로 구현한다.
구체적인 Iterator, 즉 ConcreteIterator 의 인스턴스를 만들어 낸다.

### Client side

```go
func main() {
  user1 := &user{name: "a", age: 30}
  user2 := &user{name: "b", age: 20}
  userCollection := &UserCollection{
    users: []*user{user1, user2},
  }

  iter := userCollection.createIterator()
  for iter.hasNext() {
    user := iter.getNext()
    fmt.Printf("User is %+v\n", user)
  }
}
```

Iterator pattern 을 구현한 collection 을 순회하는 코드.


## 장단점

### 장점

- 부피가 큰 순회 알고리즘을 분리하여 클라이언트 코드나 collection 관련 코드를 정리할 수 있다. (단일 책임 원칙)
- 새로운 type의 collection과 iterator를 구현하여 큰 영향 없이 기존 코드에 전달할 수 있다. (개방/폐쇄 원칙)
- 각 iterator instance 에는 고유한 반복 상태가 포함되어 있으므로 동일한 컬렉션에 대해 병렬로 반복할 수 있다.
- iterate를 지연시키거나 필요할 때 재게할 수 있다.


### 단점
- 단순한 collection 으로 사용할 수 있는 앱이라면 iterator 패턴을 적용하는 것은 과할 수 있다.
- 일부 특수한 collection 의 경우 iterator pattern 을 사용하는것이 효율적으로 작성된 자체적인 순회로직을 쓰는것 보다 느릴수 있다.


## 다른 패턴과의 관계

### Composite
> ![](https://i.imgur.com/20OApnp.png)
> 부분과 전체의 계층을 표현하기 위해 객체들을 모아 트리 구조로 구성하고 사용자로 하여금 개별 객체와 복합 객체를 모두 동일하게 다룰 수 있도록 하는 패턴



Composite 패턴은 재귀적인 구조를 갖는 패턴이기 때문에 Iterator 를 사용하여 Composite 의 tree를 순회할 수 있다.


### Factory Method
> 어떤 클래스의 인스턴스를 만들지를 서브클래스에서 결정하도록 하는 패턴


Iterator 인스턴스를 작성할 때 Factory method 패턴이 사용되는 경우가 있다.
Factory method 패턴를 사용하면 다양한 유형의 Iterator 들을 반환할 수 있다.

### Memento
> 객체 정보를 저장하고 복원하는 패턴

Memento를 Iterator와 함께 사용하여 현재 반복 상태를 캡처하고 필요에 따라 롤백할 수 있다.

### Visitor
> 많은 것이 모여 있는 내부를 돌아다니면서 같은 처리를 반복 적용해가는 패턴

Visitor와 Iterator를 함께 사용하면 복잡한 데이터 구조를 순회하면서 각 element에 대해 일부 작업을 수행할 수 있다.
모든 element가 서로 다른 클래스를 가지고 있는 경우라도 가능하다.


<!--
## 예제

페이지네이션
-->