Iteratorパターンとは
何らかのデータを集約しているオブジェクトの各要素に対して順番にアクセスするためのデザインパターン。
例えば 配列arrの要素を順番に取得する場合、 arr[0], arr[1],…arr[i],… のようにアクセスする。この変数iの働きを抽象化し、一般化したデザインパターンになる。
集約オブジェクトから「要素を走査する責務」を切り出すことで、下記のようなメリットが得られる。
- 集約オブジェクトを利用する側は集約オブジェクトが保持するデータ型を意識せずデータを走査できる。(利用側に共通の走査方法を提供できる)
→集約オブジェクトと利用側の結合度を弱めることができる。 - 複数の走査方法を集約オブジェクトに対して簡単に提供できる。
(逆順に走査する、一つ飛ばしで走査する等)
クラス図

実装
インターフェース
package main
type Aggregate interface {
Iterator() Iterator
}
package main
type Iterator interface {
HasNext() bool
Next() interface{}
}
構造体
package main
type Book struct {
name string
}
func (b *Book) GetName() string {
return b.name
}
func NewBook(name string) *Book {
book := Book{name: name}
return &book
}
package main
type BookShelf struct {
books []Book
last int
}
func NewBookShelf() *BookShelf {
bs := &BookShelf{last: 0, books: []Book{}}
return bs
}
func (b *BookShelf) GetBookAt(index int) Book {
return b.books[index]
}
func (b *BookShelf) AppendBook(book Book) {
b.books = append(b.books, book)
b.last++
}
func (b *BookShelf) GetLength() int {
return b.last
}
func (b *BookShelf) Iterator() Iterator {
return NewBookShelfIterator(*b)
}
package main
type BookShelfIterator struct {
bookShelf BookShelf
index int
}
func NewBookShelfIterator(bookShelf BookShelf) *BookShelfIterator {
b := &BookShelfIterator{bookShelf: bookShelf, index: 0}
return b
}
func (b *BookShelfIterator) HasNext() bool {
if b.index < b.bookShelf.GetLength() {
return true
} else {
return false
}
}
func (b *BookShelfIterator) Next() interface{} {
book := b.bookShelf.GetBookAt(b.index)
if b.index < b.bookShelf.GetLength() {
b.index++
}
return book
}
動作確認
package main
import (
"fmt"
)
func main() {
bs := NewBookShelf()
bs.AppendBook(*NewBook("Anime"))
bs.AppendBook(*NewBook("Bike"))
bs.AppendBook(*NewBook("Candy"))
bs.AppendBook(*NewBook("Days"))
it := bs.Iterator()
for it.HasNext() {
book := (it.Next()).(Book)
fmt.Println(book.GetName())
}
}
Anime
Bike
Candy
Days
補足
上記のBookShelfではBookをスライス(books []Book
)で保持している。これをmapで保持するようにすると以下のようになる。
実装
package main
type MapBookShelf struct {
books map[string]Book
keys []string
last int
}
func NewMapBookShelf() *MapBookShelf {
bs := &MapBookShelf{last: 0, books: map[string]Book{}}
return bs
}
func (b *MapBookShelf) GetBookAt(index int) Book {
key := b.keys[index]
return b.books[key]
}
func (b *MapBookShelf) AppendBook(book Book) {
name := book.GetName()
b.books[name] = book
b.keys = append(b.keys, name)
b.last++
}
func (b *MapBookShelf) GetLength() int {
return b.last
}
func (b *MapBookShelf) Iterator() Iterator {
return NewMapBookShelfIterator(*b)
}
package main
type MapBookShelfIterator struct {
mapBookShelf MapBookShelf
index int
}
func NewMapBookShelfIterator(bookShelf MapBookShelf) *MapBookShelfIterator {
b := &MapBookShelfIterator{mapBookShelf: bookShelf, index: 0}
return b
}
func (b *MapBookShelfIterator) HasNext() bool {
if b.index < b.mapBookShelf.GetLength() {
return true
} else {
return false
}
}
func (b *MapBookShelfIterator) Next() interface{} {
book := b.mapBookShelf.GetBookAt(b.index)
if b.index < b.mapBookShelf.GetLength() {
b.index++
}
return book
}
実行結果
package main
import (
"fmt"
)
func main() {
bs := NewBookShelf()
bs.AppendBook(*NewBook("Anime"))
bs.AppendBook(*NewBook("Bike"))
bs.AppendBook(*NewBook("Candy"))
bs.AppendBook(*NewBook("Days"))
// ここで利用しているのはIteratorオブジェクトのHasNext(), Next()のみ
// → 集合体であるBookShelfの実装に依存せずに要素の走査を行っている
it := bs.Iterator()
for it.HasNext() {
book := (it.Next()).(Book)
fmt.Println(book.GetName())
}
mbs := NewMapBookShelf()
mbs.AppendBook(*NewBook("Anime (Map)"))
mbs.AppendBook(*NewBook("Bike (Map)"))
mbs.AppendBook(*NewBook("Candy (Map)"))
mbs.AppendBook(*NewBook("Days (Map)"))
// MapBookShelfでもBookShelfと同様の方法で集合体の走査ができている
mit := mbs.Iterator()
for mit.HasNext() {
book := (mit.Next()).(Book)
fmt.Println(book.GetName())
}
}
Anime
Bike
Candy
Days
Anime (Map)
Bike (Map)
Candy (Map)
Days (Map)
データ保持をスライスからmapに変更しても同じアクセス方法でデータの走査をできることが確認できる。
コメント