lzh2nix / articles

用 issue 来管理个人博客
https://github.com/lzh2nix/articles
62 stars 13 forks source link

迭代器模式 #139

Open lzh2nix opened 3 years ago

lzh2nix commented 3 years ago

from refactoring.guru:

Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.).

2021-09-18_07-24

迭代器模式的主要功能时在不暴露内部数据结构(list, array, tree...)的情况下提供一种遍历的方法。 不然每种数据结构在client都要实现以下,比如这种:

    fmt.Println("-----------BREAKFAST-----------------------")
        // 链表类型的实现   
    for i := breakFast.Items.Front(); i != nil; i = i.Next() {
        m := i.Value.(*MenuItem)
        fmt.Println(m.Name + ", " + m.Desc + ", " + fmt.Sprintf("%f", m.Price))
    }
    // slice类型的实现
    fmt.Println("----------LUNCH-------------------")
    for _, m := range lunch.Items {
        fmt.Println(m.Name + ", " + m.Desc + ", " + fmt.Sprintf("%f", m.Price))
    }
        // map 版本实现 

        // tree 版本实现

        // stack 版本实现

如果使用 iterator模式就会是这样的:

func main(){
    fmt.Println("-----------BREAKFAST-----------------------")
    print(breakFast.Iterator())
    fmt.Println("-----------LUNCH---------------------------")
    print(lunch.Iterator())
}
func print(iter Iterator) {
    for iter.HasNext() {
        m := iter.Next()
        fmt.Println(m.Name + ", " + m.Desc + ", " + fmt.Sprintf("%f", m.Price))

    }
}

这就将大大的减少了使用方的心智负担(也为未来的扩展带来了方便)。下面就来看下具体的实现。

我们有以下的基础类:

type MenuItem struct {
    Name       string
    Desc       string
    Vegetarian bool
    Price      float64
}

type PancakeHouseMenu struct {
    Items list.List
}

func NewPackeHourseMenu() *PancakeHouseMenu {
    p := &PancakeHouseMenu{}
    p.Add("K&B's Pancake Breakfast", "Pancakes with scrambled eggs and toast", true, 2.99)
    p.Add("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99)
    p.Add("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29)
    p.Add("Hotdog", "A hot dog, with sauerkraut, relish, onions, topped with cheese", false, 3.05)
    return p
}

func (p *PancakeHouseMenu) Add(name, desc string, veg bool, price float64) {
    m := &MenuItem{
        Name:       name,
        Desc:       desc,
        Vegetarian: veg,
        Price:      price,
    }
    p.Items.PushBack(m)
}

type DinerMenu struct {
    Items []*MenuItem
}

func NewDinerMenu() *DinerMenu {
    d := &DinerMenu{}

    d.Add("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99)
    d.Add("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99)
    d.Add("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29)
    d.Add("Hotdog", "A hot dog, with sauerkraut, relish, onions, topped with cheese", false, 3.05)

    return d
}

func (d *DinerMenu) Add(name, desc string, veg bool, price float64) {
    m := &MenuItem{
        Name:       name,
        Desc:       desc,
        Vegetarian: veg,
        Price:      price,
    }
    d.Items = append(d.Items, m)
}

如果这样写我们就会暴露太多内部的实现给client侧, 在clent测又要适配各种数据结构的遍历,为了实现迭代器模式,需要添加以下的代码:

type Iterator interface {
    HasNext() bool
    Next() *MenuItem
}
func (p *PancakeHouseMenu) Iterator() Iterator {

    return &PancakeHouseMenuIterator{p.Items.Front()}
}

type PancakeHouseMenuIterator struct {
    current *list.Element
}

func (p *PancakeHouseMenuIterator) HasNext() bool {
    return p.current != nil
}
func (p *PancakeHouseMenuIterator) Next() *MenuItem {
    temp := p.current
    p.current = p.current.Next()
    return temp.Value.(*MenuItem)
}

func (p *DinerMenu) Iterator() Iterator {

    return &DinerMenuIterator{p.Items, 0}
}

type DinerMenuIterator struct {
    items []*MenuItem
    pos   int
}

func (p *DinerMenuIterator) HasNext() bool {
    return p.pos < len(p.items)
}
func (p *DinerMenuIterator) Next() *MenuItem {
    temp := p.items[p.pos]
    p.pos += 1
    return temp
}

在这种模式下新增一个Menu或者更换内部的实现方式就方便很多了。

  1. 新增只需要新增一个xxxMenu,和xxxMenuIterator 即可
  2. 内部的数据结构的修改对外是无感知的

iterator in real world

在goalng中迭代器模式最常见就是range 操作,虽然不是一个显示的迭代器模式, 但是range支持操作在各种collection上,所以是一个广义的迭代器模式。