Help us understand the problem. What is going on with this article?

IteratorパターンをJavaとJavaScriptのコードを比較して理解する

More than 1 year has passed since last update.

はじめに

詳しいことや他のパターンはデザインパターンをJavaScriptとJavaでの実装を比較して理解するに書いて行きます。
JavaScriptの例はJavaのを見て書きました。
クラス型・プロトタイプ型、型付の強弱、アクセス修飾子など特徴の違いなどは活かしていません。(必要のないgetterなどもあります)
ご了承ください。

Iteratorパターン

何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行うためのもの
iterateという英単語は何かを繰り返すという意味です。日本語では反復子と呼ばれることがある

Javaでの実装例

本棚に数冊の本があり、その本棚ごとの本の冊数を数えるプログラム

クラス図

Iterator.png

コード

Main.java
public class Main {
    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf(4);
        bookShelf.appendBook(new Book("Around the World in 80 Days"));
        bookShelf.appendBook(new Book("Bible"));
        bookShelf.appendBook(new Book("Chinderella"));
        bookShelf.appendBook(new Book("Dabby-Long-Legs"));
        Iterator it = bookShelf.iterator();
        while (it.hasNext()) {
            Book book = (Book)it.next();
            System.out.println(book.getName());
        }        
    }
}
Aggregate.java
public interface Aggregate {
    public abstract Iterator iterator();
}
BookShelf.java
public class BookShelf implements Aggregate {
    private Book[] books;
    private int last = 0;

    public BookShelf(int maxsize) {
        this.books = new Book[maxsize];
    }
    public Book getBookAt(int index) {
        return books[index];
    }
    public void appendBook(Book book) {
        this.books[last] = book;
        last++;
    }
    public int getLength() {
        return last;
    }
    public Iterator iterator() {
        return new BookShelfIterator(this);
    }
}
Book.java
public class Book {
    private String name;

    public Book(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
Iterator.java
public interface Iterator {
    public abstract boolean hasNext();
    public abstract Object next();    
}
BookShelfIterator.java
public class BookShelfIterator implements Iterator {
    private BookShelf bookShelf;
    private int index;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }
    public boolean hasNext() {
        if (index < bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    }
    public Object next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

JavaScriptの例

コード

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Iterator</title>
</head>
<body>
    <script src="Main.js"></script>
    <script src="Interface.js"></script>
    <script src="Aggregate.js"></script>
    <script src="BookShelf.js"></script>
    <script src="Book.js"></script>
    <script src="iterator.js"></script>
    <script src="BookShelfIterator.js"></script>
</body>
</html>
Main.js
MAIN = {};
MAIN.init = function() {
    var bookShelf = new BookShelf();
    bookShelf.appendBook(new Book("Around the World in 80 Days"));
    bookShelf.appendBook(new Book("Bible"));
    bookShelf.appendBook(new Book("Chinderella"));
    bookShelf.appendBook(new Book("Dabby-Long=legs"));
    var it = bookShelf.iterator();
    while (it.hasNext()) {
        var book = it.next();
        console.log(book.name);
    }
};

window.addEventListener("load", MAIN.init);
Interface.js
/**
 * @namespace INTERFACE
 */
INTERFACE = {};

/**
 * 同じプロパティを持っているか判定
 * @static
 * @method implements
 */
INTERFACE.implements = function(child, Pr) {
    var parent = new Pr();
    for (var p in parent) {
        if (!(p in child)) {
            console.error(child.constructor.name + "クラス(関数)に" + p + "メソッドが宣言されていません。");
        }
    }
};
Aggregate.js
var Aggregate = function() {};
Aggregate.prototype.iterator = function() {};
BookShelf.js
var BookShelf = function() {
    this.books = [];
    this.last = 0;

    INTERFACE.implements(this, Aggregate);
};

BookShelf.prototype = {
    constructor: BookShelf,

    getBookAt: function(index) {
        return this.books[index];
    },
    appendBook: function(book) {
        this.books[this.last] = book;
        this.last += 1;
    },
    getLength: function() {
        return this.last;
    },
    iterator: function() {
        return new BookShelfIterator(this);
    }
};
Book.js
var Book = function(name) {
    this.name = name;
};

Book.prototype.getName = function() {
    return this.name;
};
Iterator.js
var Iterator = function() {};
Iterator.prototype = {
    constructor: Iterator,

    hasNext: function() {},
    next: function() {}
};
BookShelfIterator.js
var BookShelfIterator = function(bookShelf) {
    this.bookShelf = bookShelf;
    this.index = 0;

    INTERFACE.implements(this, Iterator);
};

BookShelfIterator.prototype = {
    constructor: BookShelfIterator,

    hasNext: function() {
        if (this.index < this.bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    },
    next: function() {
        var book = this.bookShelf.getBookAt(this.index);
        this.index += 1;
        return book;
    }
}

ある本に書いてあったJavaScriptでのIteratorパターン

Iteratorパターンは振る舞いなので、hasNext()とnext()があればいいってことなんですかね?
自分は再利用性が出来ない感じがするのでちょっと。。。って感じです
参考までに

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Iterator</title>
</head>
<body>
    <script src="Main.js"></script>
</body>
</html>
Main.js
MAIN = {};

MAIN.init = function() {
    while(egg.hasNext()) {
        console.log(egg.next());
    }
};

var egg = (function() {
    var index = 0,
        data = [1, 2, 3, 4, 5],
        length = data.length;

    return {
        next: function() {
            var element;
            if (!this.hasNext()) {
                return null;
            }
            element = data[index];
            index = index + 1;
            return element;
        },

        hasNext: function() {
            return index < length;
        }
    };
}());

window.addEventListener("load", MAIN.init);

Iteratorパターンの登場人物

Iterator(反覆子)の役

要素を順番にスキャンしていくインタフェースを定める役
サンプルプログラム⇒Iterator(interface)

ConcreateIterator(具体的な反復子)の役

Iterator役が定めたインタフェースを実際に実装する役
サンプルプログラム⇒BookShelfIterator(class)

Aggregate(集合体)の役

Iterator役を作り出すインターフェースを定める役
サンプルプログラム⇒Aggregate(interface)

ConcreateAggregate(具体的な集合体)の役

Aggregate役が定めたインタフェースを実際に実装する役
サンプルプログラム⇒BookShelf(class)

Iteratorパターンのクラス図

クラス図2.png

Iteratorパターンの必要性

Iteratorパターンを使わなくても、for文などで回せばいいと思える
Iteratorを使う大きな理由は、Iteratorを使うことで、実装とは切り離して、数え上げを行うことが出来るからである

イテレータの種類

今回の例では順方向に一度だけスキャンする単純なものだが、他にもある

  • 最後尾から開始して逆方向に進む
  • 順方向にも逆方向にも行く(next, previousメソッドの両方を持つ)
  • 番号を指定して、そこから始める
  • etc...

関連しているパターン

参考

増補改訂版Java言語で学ぶデザインパターン入門
JavaScriptパターン――優れたアプリケーションのための作法

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away