LoginSignup
6
3

More than 3 years have passed since last update.

Golangで、デザインパターン「Iterator」を学ぶ

Last updated at Posted at 2020-03-28

GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Iterator」を学ぶ"

今回は、Pythonで実装した”Iterator”のサンプルアプリをGolangで実装し直してみました。

■ Iterator(イテレータ・パターン)

Iteratorパターン(イテレータ・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。

UML class and sequence diagram

W3sDesign_Iterator_Design_Pattern_UML.jpg

UML class diagram

iterator.png
(以上、ウィキペディア(Wikipedia)より引用)

□ 備忘録

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インタフェースが、この役を努めます。

iterator/iterator.go
package iterator

// Iterator is interface
type Iterator interface {
    HasNext() bool
    Next() *Book
}

(2) ConcreteIterator(具体的な反復子)の役

Iterator役で定めたインタフェースを実際に実装する役です。
サンプルプログラムでは、bookShelfIterator構造体が、この役を努めます。
この役は、スキャンするために必要な情報を持っている必要があります。
サンプルプログラムでは、BookShelf構造体のインスタンスを、メンバー変数bookShelfで覚えており、注目している本をメンバー変数indexで覚えるようになっています。

iterator/book.go
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インタフェースが、この役を努めます。

iterator/aggregate.go
package iterator

// Aggregate is interface
type Aggregate interface {
    Append(book *Book)
    Iterator() Iterator
}

(4) ConcreteAggregate(具体的な集合体)の役

Aggregate役が定めたインタフェースを実際に実装する役です。具体的なIterator役、すなわちConcreteIterator役のインスタンスを作り出します。
サンプルプログラムでは、BookShelf構造体が、この役を努めます。

iterator/book.go
// 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関数が、この役を努めます。

Main.go
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)その他

本のタイトルを管理します。

iterator/book.go
// 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
}

■ 参考URL

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3