# 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가 서로 다른 클래스를 가지고 있는 경우라도 가능하다. <!-- ## 예제 페이지네이션 -->