Design Pattern 筆記 - Iterator Pattern
01 Jan 2019Post Directory
前言
前陣子在天瓏買了本『圖解設計模式』,家中的書越積越多了,新年新希望,來個系列文章把這本書涵蓋的 Pattern 給填完好了 (つд⊂)
本文使用的參考書籍範例使用 Java,而筆者則是使用 Golang 實作,因此描述跟實作上可能會有些出入
Iterator Pattern
常見的遍歷多以 for 迴圈來實作
arr := []string{"1", "2", "3"}
for _, item := range arr {
fmt.Println(item)
}
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
以下面的 for 範例來說,i 會不斷 ++ 循環來遍歷 arr 陣列中的元素,將 i 的功能抽象化後、通用化後就變成了 Iterator Pattern 了
此範例實作一個書架,並將書放到書架上,然後遍歷整個架上的書本,下面範例會直接貼上書上的 java 範例程式和我寫的 golang 程式碼
Class Diagram
name | descreption |
---|---|
Aggregate | 該 pattern 所用到的 Interface 集合 |
Iterator | 遍歷的 interface |
Book | 書的 class |
BookShelf | 書架的 class |
BookShelfIterator | 遍歷書架的 class |
範例
Interface
Aggregate
Iterator Pattern 功能的集合(只有 Iterator 的 Interface)
java:
public interface Aggregate {
public abstract Iterator iterator();
}
go:
type Aggregate interface {
Iterator() Iterator
// 這邊會多個 Append 為了方便 Aggregate 的 instance(bookShelf) 可以直接使用 Append 這個 Method
// Iterator Pattern 並不存在 Append 的 interface
Append(interface{})
}
Interator
該 Interator(遍歷器)最主要有兩個 method:
- HasNext():是否有下一個 item() 回傳 true/false 用以決定是否停止遍歷
- Next():回傳當下的 Book 並指向下一個 Book
java:
public interface Iterator {
public abstract boolean hasNext();
public abstract Object next();
}
go:
type Iterator interface {
HasNext() bool
Next() interface{}
}
Implement
Book class
作為該範例中最基本的單位,不過在 Golang 的世界中沒有 Class,所以這邊我把它當作是 struct
java:
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
go:
package book
type BookI interface {
SetName(n string)
GetName() string
}
type Book struct {
name string
}
func (b *Book) SetName(n string) {
b.name = n
}
func (b Book) GetName() string {
return b.name
}
BookShelf class
書架,紀錄書架內的書本資訊
java:
public class BookShelf implements Aggregate {
private Book[] books;
private int last = 0;
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
go:
package model
import (
"design_pattern/1.iterator/book"
)
type Aggregate interface {
Interator() Interator
}
type BookShelf struct {
books []book.Book
last int
}
func (b *BookShelf) BookShelf(maxsize int) {
b.books = make([]book.Book, maxsize)
}
func (b *BookShelf) GetBookAt(index int) book.Book {
return b.books[index]
}
func (b *BookShelf) Append(abook interface{}) {
if b.GetLen() >= len(b.books) {
// 這邊會 new 一個新空間會 *2 並沒有什麼特別的考量與設計,單純是實作時想到了另一篇關於 slice 文章的利自有用也是這樣做
// https://blog.golang.org/go-slices-usage-and-internals
newBooks := make([]book.Book, b.last*2)
for index, item := range b.books {
newBooks[index] = item
}
b.books = newBooks
}
// 這邊沒使用 append 來塞值的原因在於,BookShelf 在 new 完一個 array 在使用 append 塞值,就會從新 array 後塞入
// EX:
// BookShelf(3) 後 [book.Book{}, book.Book{}, book.Book{}]
// append 新值 [book.Book{}, book.Book{}, book.Book{}, book.Book(新值)]
b.books[b.last] = abook.(book.Book)
b.last++
}
func (b *BookShelf) GetLen() int {
return b.last
}
func (b *BookShelf) Iterator() Iterator {
i := &BookShelfIterator{}
i.BookShelfIterator(b)
return i
}
BookShelfIterator
書架遍歷器的 Instance
java:
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookshelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
public boolean hasNext() {
if(index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
go:
package model
type Iterator interface {
HasNext() bool
Next() interface{}
}
type BookShelfIterator struct {
bookShelf BookShelf
index int
}
func (b *BookShelfIterator) BookShelfIterator(bookShelf *BookShelf) BookShelfIterator {
b.bookShelf = *bookShelf
b.index = 0
return *b
}
func (b *BookShelfIterator) HasNext() bool {
if b.index < b.bookShelf.GetLen() {
return true
}
return false
}
func (b *BookShelfIterator) Next() interface{} {
book := b.bookShelf.GetBookAt(b.index)
b.index++
return book
}
Main
java:
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the World in 80 Days"))
bookShelf.appendBook(new Book("Bible"))
bookShelf.appendBook(new Book("Cinderella"))
bookShelf.appendBook(new Book("Daddy-Long-Legs"))
Iterator it = bookShelf.iterator();
while (it.hasNext()) {
Book book = (Book)it.next();
System.out.println(book.getName());
}
}
}
go:
package main
import (
"fmt"
"design_pattern/1.iterator/book"
"design_pattern/1.iterator/model"
)
func main() {
var bookShelf model.Aggregate
b := &model.BookShelf{}
b.BookShelf(3)
bookShelf = b
aBook := &book.Book{}
aBook.SetName("Around the World in 80 Days")
bookShelf.Append(*aBook)
bBook := &book.Book{}
bBook.SetName("Bible")
bookShelf.Append(*bBook)
cBook := &book.Book{}
cBook.SetName("Cinderella")
bookShelf.Append(*cBook)
dBook := &book.Book{}
dBook.SetName("Daddy-Long-Legs")
bookShelf.Append(*dBook)
it := bookShelf.Iterator()
for it.HasNext() {
book := it.Next().(book.Book)
fmt.Println(book.GetName())
}
}
So Why?
不管 Instance 如何變化,都可以使用 Iterator
it := bookShelf.Interator()
for it.HasNext() {
book := it.Next().(book.Book)
fmt.Println(book.GetName())
}
while 的迴圈不依賴於 bookShelf 的 Instance,所以如果 bookShelf 的 books 不用 array 存了,或是 BookShelf, Book, BookShelfIterator 改成其他的資料型態的 Instance(EX: Warehouse, Box, WarehouseIterator),也不會影響到原本 for it.HasNext()
內的邏輯
範例 2
bookShelf 的 Books 改用 String 存了
type BookShelf struct {
books string
last int
}
func (b *BookShelf) BookShelf(maxsize int) {
// b.books = make([]book.Book, maxsize)
}
func (b *BookShelf) GetBookAt(index int) book.Book {
s := strings.Split(b.books, ",")
aBook := book.Book{}
aBook.SetName(s[index])
return aBook
}
func (b *BookShelf) Append(abook interface{}) {
if len(b.books) == 0 {
b.books = abook.(book.Book).GetName()
} else {
b.books = b.books + "," + abook.(book.Book).GetName()
}
}
func (b *BookShelf) GetLen() int {
s := strings.Split(b.books, ",")
return len(s)
}
func (b *BookShelf) Iterator() Iterator {
i := &BookShelfIterator{}
i.BookShelfIterator(b)
return i
}
這邊直接改掉 BookShelf 格式和相關 Instance 就好,舊有的 code 都不需要改
結語
文中的 Source Code 都放在 github 了,自己 clone 下來玩玩吧
小弟新手 gopher,這也是第一次看 design pattern 所以如果有任何錯誤還請幫忙指正
目標是兩個禮拜發一篇啦,這邊累積的文章太少了,不過拖更什麼的也是很正常的 _(:3 ⌒゙)_