Iterator pattern traverses through aggregate items. Using this pattern we can establish a standard for traversal and the traversal of the items becomes easy.
In this article, we are demonstrating Iterator pattern implementation in Golang. Check the implementation details and the examples.
Implementation
Follow the steps below to implement Iterator pattern in Golang-
- Create an interface for the Iterator. Declare one method for checking if there is any item available for the next iteration, and another method for getting the next item. There can be other method declaration as per the requirement.
- Create iterator struct, that implements the interface. Define field for storing the list of items, and the index of current items.
- Create another struct that uses the iterator. This struct has fields for storing the list of items and a method that creates a new iterator and returns that.
- While using the implementation we can the data struct and get a new iterator. Then we can use the methods of the iterator.
Here is a simple example of the implementation-
// Iterator pattern in Golang
package main
import "fmt"
// Iterator interface
type Iterator[T any] interface {
HasNext() bool
Next() T
}
// Custom iteratory
type MyIterator[T any] struct {
index int
items []T
}
func NewMyIterator[T any](items []T) (myIterator *MyIterator[T]) {
myIterator = &MyIterator[T]{}
myIterator.items = items
return
}
func (myIterator *MyIterator[T]) HasNext() bool {
return myIterator.index < len(myIterator.items)
}
func (myIterator *MyIterator[T]) Next() T {
if myIterator.index >= len(myIterator.items) {
panic("no more item")
}
currentIndex := myIterator.index
myIterator.index += 1
return myIterator.items[currentIndex]
}
// Data struct that uses the custom iterator
type Data[T any] struct {
items []T
}
func NewData[T any](items []T) (data *Data[T]) {
data = &Data[T]{}
data.items = items
return
}
func (data *Data[T]) GetIterator() Iterator[T] {
return NewMyIterator(data.items)
}
// Demo
func main() {
items := []string {
"test1",
"test2",
"test3",
}
data := NewData[string](items)
iterator := data.GetIterator()
for iterator.HasNext() {
fmt.Println(iterator.Next())
}
}
Output will be as below-
test1
test2
test3
Examples
Here are a few examples. Take a look-
Example #1: Pagination Iterator
Let’s take the example of iterating and showing pagination items.
Page Item Struct [General]
This struct is for general use. It is not directly related to the iterator implementation. The “Page” struct represents a single item for representing an element of pagination.
- Create file “page.go”.
- Create struct “Page”.
- Create method “NewPage”. This method initiates a new “Page” and returns the pointer.
- We have some utility methods- “GetNumber” and “GetPath”, implemented for the struct.
// page.go
package main
import "fmt"
type Page struct {
number int
path string
}
func NewPage() (page *Page) {
page = &Page{}
return
}
func (page *Page) GetNumber() int {
return page.number
}
func (page *Page) GetPath() string {
if page.path == "" {
return fmt.Sprintf("/page/%v", page.number)
}
return page.path
}
func (page *Page) SetNumber(number int) {
page.number = number
}
func (page *Page) SetPath(path string) {
page.path = path
}
Page List Interface
- Create file “abstract_page_list.go”.
- Create interface “AbstractPageListInterface”.
- Declare methods “Add”, for adding a new page element to the list.
- Declare method “Remove”, for removing a page element from list.
- Declare method “Iterator” that returns an iterator.
// abstract_page_list.go
package main
type AbstractPageList interface {
Add(page *Page)
Iterator() *AbstractIterator
Remove(page *Page)
}
Page List Struct
- Create file “page_list.go”.
- Create struct “PageList”.
- Declare a field named “pages” of type of slice of “Page”. This field will be used to store the list of pages.
- Define method “NewPageList”. This method initiates a new “PageList” and returns the pointer.
- Implement interface “AbstractPageList” for the struct. Define method “Add”, “Remove”, “Iterator” for the struct, as part of the interface implementation.
// page_list.go
package main
type PageList struct {
pages []Page
}
func NewPageList() (pageList *PageList) {
pageList = &PageList{}
return
}
func (pageList *PageList) Add(page Page) {
pageList.pages = append(pageList.pages, page)
}
func (pageList *PageList) Remove(page Page) {
for i, pageElem := range pageList.pages {
if pageElem == page {
pageList.pages = append(pageList.pages[:i], pageList.pages[i+1:]...)
break;
}
}
}
func (pageList *PageList) Iterator() AbstractIterator {
return NewIterator(pageList.pages)
}
Iterator Interface
- Create file “abstract_iterator.go”.
- Define interface “AbstractIterator”.
- Declare method “HasNext”. Purpose of this method is to check if any item remains while traversing the list of items. This method returns a boolean.
- Declare method “Next”. This method will be used to get the next item(when implemented).
// abstract_iterator.go
package main
type AbstractIterator interface {
HasNext() bool
Next() Page
}
Iterator Struct
- Create file “iterator.go”.
- Create struct “Iterator”.
- Declare field “currentPosition”, which is used to track the last element which is accessed from the list.
- Define field “page”, which is a slice of “Page”. This is used to store the full list of pages.
- Define method “NewIterator”. This accepts a list (slice) of “Page” items, initates a new “Iterator” and set the initial values.
- Implement the “AbstractIterator” for the “Iterator” struct. Define methods “HasNext” and “Next” for the struct.
// iterator.go
package main
type Iterator struct {
currentPosition int
pages []Page
}
func NewIterator(pages []Page) (iterator *Iterator) {
iterator = &Iterator{}
iterator.currentPosition = 0
iterator.pages = pages
return
}
func (iterator *Iterator) HasNext() bool {
return iterator.currentPosition < len(iterator.pages)
}
func (iterator *Iterator) Next() Page {
page := iterator.pages[iterator.currentPosition]
iterator.currentPosition++
return page
}
Demo
For using the implementation we need a list of pages. We are using the method “populatePageList” for generating a list of pages (this is a dummy method). This method creates a new “PageList” struct, add items to the list, and then returns that.
Then we are getting the “Iterator” method of the “pageList” to obtain the iterator.
Then we can use a loop that runs until there are items available in the list(use “HasNext” method to check that). And then get the next item using “Next” method.
// main.go
package main
import "fmt"
func main() {
pageList := populatePageList()
paginator := pageList.Iterator()
for paginator.HasNext() {
page := paginator.Next()
fmt.Printf("Page Number: %vn", page.GetNumber())
fmt.Printf("Page Path: %vnn", page.GetPath())
}
}
// dummy method for generating a list of pages
func populatePageList() *PageList {
pageList := NewPageList()
for i := 0; i < 10; i++ {
page := NewPage()
page.SetNumber(i)
pageList.Add(*page)
}
return pageList
}
Output
Page Number: 0
Page Path: /page/0
Page Number: 1
Page Path: /page/1
Page Number: 2
Page Path: /page/2
Page Number: 3
Page Path: /page/3
Page Number: 4
Page Path: /page/4
Page Number: 5
Page Path: /page/5
Page Number: 6
Page Path: /page/6
Page Number: 7
Page Path: /page/7
Page Number: 8
Page Path: /page/8
Page Number: 9
Page Path: /page/9
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Pagination Iterator | GitHub |
Other Code Implementations
Use the following links to check the Iterator pattern implementation in other programming languages.