デザインパターン　Iteratorパターン

# Iteratorパターン
コレクションや配列などのオブジェクトの各要素に対して、順次アクセスする方法を提供するためのデザインパターン。

コレクションの内部構造が隠蔽されることで、クライアントは、コレクションの各要素が実際にどんな型なのか、などの内部構造を意識することなく、各要を順番に取り出して繰り返し処理を行うことが可能になる。

## Iteratorパターンが利用できるシチュエーション
あるクラス（`Item`）があり、店舗Aは配列（`Item[]`）で保持し、店舗Bはリスト（`ArrayList`）で保持しているとする。
```java:Item.java
public class Item {
    String name;
    int price;

    public Item(String name, int price){
        this.name = name;
        this.price = price;
    }
}
```

```java:ShopA.java
public class ShopA {
    static final int MAX_COUNT = 10;
    int index = 0;
    private Item[] items; // 配列で保持している

    public ShopA() {
        items = new Item[MAX_COUNT];
        add(new Item("製品A", 1000));
        add(new Item("製品B", 2000));
        add(new Item("製品C", 3000));
    }

    void add(Item product) {
        if (MAX_COUNT <= index) {
            System.out.println("これ以上追加できません。");
        } else {
            items[index] = product;
            index++;
        }
    }
}
```



```java:ShopB.java
import java.util.ArrayList;
import java.util.List;

public class ShopB {
    private List<Item> items; // リストで保持している

    public ShopB() {
        items = new ArrayList<>();
        add(new Item("商品A", 100));
        add(new Item("商品B", 200));
        add(new Item("商品C", 300));
    }

    void add(Item item) {
        items.add(item);
    }
}
```
このような状況の時、`ShopA`と`ShopB`を利用するクライアント（`Client`）は、`Item`が配列で保持されているのか、リストで保持されているのかに応じて、処理を記述する必要がある。

`for`文を記述するにしても、コレクションの要素数は配列には`length`、リストには`size()`という具合に使い分ける必要がある。

このような状況ではIteratorパターンを利用することで、クライアントはこれらの内部構造の違いを意識せずに`ShopA`と`ShopB`を利用できるようになる。

## 必要なインターフェース
Iteratorパターンを実現するには以下の２つのインターフェースが必要。

```java:Iterator.java
public interface Iterator<T> {
    boolean hasNext();
    T next();
    void remove();
}
```

```java:Aggregate.java
public interface Aggregate {
    Iterator<T> iterator();
}
```

## インターフェースの実装
まずは、`Iterator`を実装した`ShopAIterator`と`ShopBIterator`を作成する。
```java:ShopAIterator.java
public class ShopAIterator implements Iterator<Item> {
    Item[] items;
    int position = 0;

    ShopAIterator(Item[] items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        if (position >= items.length || items[position] == null) {
            return false;
        }

        return true;
    }

    @Override
    public Item next() {
        if (!hasNext()) {
            throw new IllegalStateException("次の要素は存在しません。");
        }

        return items[position++];
    }

    @Override
    public void remove() {
        if (position <= 0) {
            throw new IllegalStateException("削除できません。");
        }

        // メモ
        // position = 5
        // 処理前：1,2,3,4,5,6,7,8,9,10
        // 処理後：1,2,3,4,5,7,8,9,10,10
        for (int i = position; i < items.length - 1; i++) {
            items[i] = items[i + 1];
        }

        // 処理前：1,2,3,4,5,6,7,8,9,10
        // 処理後：1,2,3,4,5,7,8,9,10,null
        items[items.length - 1] = null;

//        items[i + 1]が配列の範囲外にアクセスしてしまうためNG
//        for(int i = position; i < items.length; i++ ){
//            items[i] = items[i + 1];
//        }
    }
}

```

```java:ShopBIterator.java
import java.util.List;

public class ShopBIterator implements Iterator<Item> {
    List<Item> items;
    int position = 0;

    public ShopBIterator(List<Item> items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        return position + 1 < items.size();
    }

    @Override
    public Item next() {
        if (hasNext()) {
            return items.get(position++);
        }

        return null;
    }

    @Override
    public void remove() {
        if (position < 0 || position >= items.size()) {
            throw new IllegalStateException("削除できません。");
        }

        items.remove(position);
        // 要素が削除されたため、-1しないと次のnext()時に、要素を一つスキップさせることになる
        position--;
    }
}

```

店舗Aと店舗Bに`Aggregate`を実装する。

```java:ShopA.java
public class ShopA implements Aggregate<Item> {
    // 一部省略
    
    @Override
    public Iterator<Item> iterator() {
        return new ShopAIterator(items);
    }
}
```
```java:ShopB.java
public class ShopB implements Aggregate<Item> {
    // 一部省略

    @Override
    public Iterator<Item> iterator() {
        return new ShopBIterator(items);
    }
}

```

これにより、クライアントは店舗A、店舗 Bの内部構造を意識する必要がなくなる。
## Iteratorパターンの利用
```java:Client.java
public class Client {
    ShopA shopA;
    ShopB shopB;

    public Client(ShopA shopA,ShopB  shopB) {
        this.shopA = shopA;
        this.shopB = shopB;
    }

    // 異なる構造を持つ店舗Aと店舗Bを、全く同じように扱うことができている
    public void print() {
        Iterator iteratorA = shopA.iterator();
        Iterator iteratorB = shopB.iterator();

        System.out.println("shopAの商品一覧です。");
        print(iteratorA);

        System.out.println("shopBの商品一覧です。");
        print(iteratorB);
    }

    private void print(Iterator iterator){
        // インターフェースに対して実装できている
        while(iterator.hasNext()){
            Item next = (Item)iterator.next();
            System.out.println(next.name);
        }
    }
}
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        Client client = new Client(new ShopA(), new ShopB());
        client.print();
        // shopAの商品一覧です。
        // 製品A
        // 製品B
        // 製品C
        // shopBの商品一覧です。
        // 商品A
        // 商品B
    }
}
```
## クラス図
![itartor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/30c22e86-c2d9-3888-a736-7651b73caf4b.png)


## ポイント
+ リスト、配列、マップなどのオブジェクトの集合は、**コレクション**または**アグリゲート**（Aggregate：集約）と呼ばれる。

+ `remove()`は、機能として提供したくない場合があり、その場合には、実行時例外`java.lang.UnsupportedOperationException`を発行するようにする。また、`remove()`を呼び出す際には、適切に例外チェックを行う。
+ マルチスレッド環境では、`remove()`は同じタイミングで同じコレクションを参照してしまうことがあるため、注意して設計を行う。

## Null Iterator
`null`を返す代わりに何もしないIteratorを返すクラス。ツリー構造を扱う際などに、Compositeパターンと併せて使用される。
```java:Java
public class NullIterator implements Iterator<Item> {
    public Item next(){
        return null;
    }

    public boolean hasNext(){
        return false;
    }
    
    public void remove(){
        throw new UnsupportedOperationException();
    }
}
```
## 参考
・<a href="https://amzn.asia/d/8MCJXIB" target="_blank" rel="noopener noreferrer">Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本</a>