saeki’s blog

The limits of my code mean the limits of my world.

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

こちらを読みながら進める。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

Iteratorパターンとは

コレクションに対する繰り返し処理で使われるパターン。コレクションオブジェクトとイテレータを分離し、イテレータをinterfaceで抽象化することで、利用側がコレクションのデータ構造に依存することなくiterate処理を行えるようになるというもの。
登場人物の関係をシンプルに書くとこんな感じ。

f:id:t_saeki:20190621213908p:plain

集約とイテレータを分離するために集約の実装には Iterator()が要求され、イテレーションにおける具体的なコレクション操作はIteratorインターフェースを実装したImplementIteratorに委譲される。ので、利用側は集約への依存ではなくIteratorインターフェースへの依存によってiterate処理を行うことができる。

実装

増補改訂版Java言語で学ぶデザインパターン入門で使われてる本棚の例をGoで実装してみる。
要件は、本(Entity)を格納する本棚(Aggregate)を作り、main処理で本棚に入っている本の名前を順番に表示するというもの。

ディレクトリ構成は以下

├── bookshelf
│   ├── Aggregate.go
│   ├── Book.go
│   ├── Bookshelf.go
│   ├── Bookshelfiterator.go
│   ├── Iterator.go
│   └── Product.go
└── main.go

Aggregate

Aggregateインターフェース。

package bookshelf

type Aggregate interface {
    Iterator() *Iterator
}

ImplementAggregate。簡単な配列操作とIterator()の実装

package bookshelf

type bookshelf struct {
    books []Product
    last  int
}

func NewBookShelf(maxsize int) *bookshelf {
    return &bookshelf{books: make([]Product, maxsize), last: 0}
}

func (bs *bookshelf) GetByIndex(index int) Product {
    return bs.books[index]
}

func (bs *bookshelf) AppendBook(b Product) {
    bs.books[bs.last] = b
    bs.last++
}

func (bs *bookshelf) GetLength() int {
    return bs.last
}

func (bs *bookshelf) Iterator() Iterator {
    return NewBookshelfIterator(bs)
}

Iterator

Iteratorインターフェース。このあたりは要求に合わせて変えていけば良さそう

package bookshelf

type Iterator interface {
    HasNext() bool
    Next() Product
}

ImplementIteratorの実装。

package bookshelf

type bookshelfIterator struct {
    bookshelf Aggregate
    index     int
}

func NewBookshelfIterator(bookshelf Aggregate) Iterator {
    return &bookshelfIterator{bookshelf: bookshelf, index: 0}
}

func (it *bookshelfIterator) HasNext() bool {
    return it.index < it.bookshelf.GetLength()
}

func (it *bookshelfIterator) Next() Product {
    book := it.bookshelf.GetByIndex(it.index)
    it.index++
    return book
}

Entity

Entityを表すProductインターフェース。ここまで厳密な型を用意しなくていいかも

package bookshelf

type Product interface {
    GetName() string
}
package bookshelf

type book struct {
    name string
}

func NewBook(name string) *book {
    return &book{name}
}

func (b *book) GetName() string {
    return b.name
}

main

main処理

package main

import (
    "fmt"

    "github.com/saekis/go-design-pattern/1_iterator/bookshelf"
)

func main() {
    bs := bookshelf.NewBookShelf(3)
    bs.AppendBook(bookshelf.NewBook("リーダブルコード"))
    bs.AppendBook(bookshelf.NewBook("達人プログラマ"))
    bs.AppendBook(bookshelf.NewBook("Team Geek"))
    it := bs.Iterator()
    for it.HasNext() {
        book := it.Next()
        fmt.Println(book.GetName())
    }
}

実行結果

$ go run main.go 
リーダブルコード
達人プログラマ
Team Geek

サンプル実装したクラス図

f:id:t_saeki:20190622222126p:plain

実装したもの置き場

github.com