GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Iterator」を学ぶ"
今回は、Pythonで実装した”Iterator”のサンプルアプリをGolangで実装し直してみました。
■ Iterator(イテレータ・パターン)
Iteratorパターン(イテレータ・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。
UML class and sequence diagram
UML class diagram
□ 備忘録
Iteratorパターンとは、何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行うためのものだそうです。Iterator
は、日本語で、反復子と呼ばれることもあるそうです。
Pythonプログラミングに携わっていると、よくお目にかかるやつですね。
■ "Iterator"のサンプルプログラム
実際に、Iteratorパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
- 本棚に、
Aroun d the World in 80 days
の本を追加する - 本棚に、
Bible
の本を追加する - 本棚に、
Cinderella
の本を追加する - 本棚に、
Daddy-Long-Legs
の本を追加する - 現在、本棚に存在する本のタイトルを表示する
$ go run Main.go
Aroun d the World in 80 days
Bible
Cinderella
Daddy-Long-Legs
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Iterator
- ディレクトリ構成
.
├── Main.go
└── iterator
├── aggregate.go
├── book.go
└── iterator.go
(1) Iterator(反復子)の役
要素を順番にスキャンしていくインタフェースを定める役です。
サンプルプログラムでは、Iterator
インタフェースが、この役を努めます。
package iterator
// Iterator is interface
type Iterator interface {
HasNext() bool
Next() *Book
}
(2) ConcreteIterator(具体的な反復子)の役
Iterator
役で定めたインタフェースを実際に実装する役です。
サンプルプログラムでは、bookShelfIterator
構造体が、この役を努めます。
この役は、スキャンするために必要な情報を持っている必要があります。
サンプルプログラムでは、BookShelf
構造体のインスタンスを、メンバー変数bookShelf
で覚えており、注目している本をメンバー変数index
で覚えるようになっています。
type bookShelfIterator struct {
bookShelf *BookShelf
index int
}
func newBookShelfIterator(bookShelf *BookShelf) *bookShelfIterator {
return &bookShelfIterator{
bookShelf: bookShelf,
index: 0,
}
}
// HasNext func for checking condition
func (b *bookShelfIterator) HasNext() bool {
if b.index < b.bookShelf.getLength() {
return true
}
return false
}
// Next func for fetching book
func (b *bookShelfIterator) Next() *Book {
book := b.bookShelf.getBookAt(b.index)
b.index++
return book
}
(3) Aggregate(集合体)の役
Iterator
役を作り出すインタフェースを定める役です。そのインタフェースは、「私が持っている要素を順番にスキャンしてくれる人」を作り出す抽象化メソッドということになります。
サンプルプログラムでは、Aggregate
インタフェースが、この役を努めます。
package iterator
// Aggregate is interface
type Aggregate interface {
Append(book *Book)
Iterator() Iterator
}
(4) ConcreteAggregate(具体的な集合体)の役
Aggregate
役が定めたインタフェースを実際に実装する役です。具体的なIterator
役、すなわちConcreteIterator
役のインスタンスを作り出します。
サンプルプログラムでは、BookShelf
構造体が、この役を努めます。
// BookShelf is struct
type BookShelf struct {
last int
books []*Book
}
// NewBookShelf func for initializing NewBookShelf
func NewBookShelf() *BookShelf {
return &BookShelf{
last: 0,
}
}
func (b *BookShelf) getBookAt(index int) *Book {
return b.books[index]
}
// Append func for adding book
func (b *BookShelf) Append(book *Book) {
b.books = append(b.books, book)
b.last++
}
func (b *BookShelf) getLength() int {
return b.last
}
// Iterator func for iterating BookShelfIterator
func (b *BookShelf) Iterator() Iterator {
return newBookShelfIterator(b)
}
(5) Client(依頼人)の役
サンプルプログラムでは、startMain
関数が、この役を努めます。
package main
import (
"fmt"
"./iterator"
)
func startMain(bookShelf iterator.Aggregate) {
bookShelf.Append(iterator.NewBook("Aroun d the World in 80 days"))
bookShelf.Append(iterator.NewBook("Bible"))
bookShelf.Append(iterator.NewBook("Cinderella"))
bookShelf.Append(iterator.NewBook("Daddy-Long-Legs"))
it := bookShelf.Iterator()
for it.HasNext() {
book := it.Next()
fmt.Println(book.GetName())
}
}
func main() {
startMain(iterator.NewBookShelf())
}
(6)その他
本のタイトルを管理します。
// Book is struct
type Book struct {
name string
}
// NewBook func for initializing Book
func NewBook(name string) *Book {
return &Book{
name: name,
}
}
// GetName func for getting bookname
func (b *Book) GetName() string {
return b.name
}