1. Iterator Pattern là gì

Mẫu iterator là một mẫu thiết kế hành vi cung cấp một phương pháp đồng nhất để duyệt các phần tử của một đối tượng tập hợp mà không tiết lộ cấu trúc nội bộ của đối tượng tập hợp.

2. Đặc điểm và Ưu điểm của Iterator Pattern

Các đặc điểm và ưu điểm của mẫu iterator như sau:

  • Nó có thể ẩn cấu trúc nội bộ của đối tượng tập hợp, tách rời thuật toán duyệt từ đối tượng tập hợp.
  • Nó cung cấp một cách chuẩn hóa để duyệt qua các loại đối tượng tập hợp khác nhau.
  • Nó đơn giản hóa mã nguồn của khách hàng, làm cho mã nguồn trở nên rõ ràng và ngắn gọn hơn.
  • Nó có thể cung cấp các triển khai khác nhau của trình lặp để thích nghi với các nhu cầu duyệt khác nhau.

3. Ví dụ về Ứng dụng Thực tế của Iterator Pattern

Mẫu iterator có nhiều ứng dụng thực tế, chẳng hạn như:

  • Duyệt qua kết quả truy vấn cơ sở dữ liệu.
  • Duyệt qua tệp và thư mục trong hệ thống tệp.
  • Duyệt qua các phần tử trong một tập hợp.

4. Triển khai của mẫu Iterator trong Golang

4.1 Sơ đồ lớp UML

Mẫu Iterator trong Golang

4.2 Giới thiệu Ví dụ

Trong sơ đồ lớp UML ở trên, chúng ta có hai vai trò chính: Iterator và Collection.

  • Iterator xác định giao diện để duyệt qua một đối tượng tập hợp, bao gồm phương thức HasNext() để xác định xem có phần tử nào khác không và phương thức Next() để lấy phần tử kế tiếp.
  • ConcreteIterator là lớp cụ thể của Iterator, triển khai các phương thức HasNext()Next().
  • Collection xác định giao diện để tạo đối tượng trình lặp, với phương thức CreateIterator().
  • ConcreteCollection là lớp cụ thể của Collection, triển khai phương thức CreateIterator().

4.3 Bước triển khai

Tiếp theo, chúng ta sẽ triển khai mẫu Iterator trong Golang từng bước một.

4.3.1 Xác định Giao diện Iterator và Lớp Cụ thể Iterator

type Iterator interface {
    HasNext() bool
    Next() interface{}
}

type ConcreteIterator struct {
    collection *ConcreteCollection
    index      int
}

func (it *ConcreteIterator) HasNext() bool {
    if it.index < len(it.collection.items) {
        return true
    }
    return false
}

func (it *ConcreteIterator) Next() interface{} {
    if it.HasNext() {
        item := it.collection.items[it.index]
        it.index++
        return item
    }
    return nil
}

4.3.2 Xác định Giao diện Đối tượng có thể Lặp và Lớp Đối tượng có thể Lặp Cụ thể

type Collection interface {
    CreateIterator() Iterator
}

type ConcreteCollection struct {
    items []interface{}
}

func (c *ConcreteCollection) CreateIterator() Iterator {
    return &ConcreteIterator{
        collection: c,
        index:      0,
    }
}

4.3.3 Triển khai Logic tạo trình lặp trong Lớp Đối tượng có thể Lặp

func main() {
    collection := &ConcreteCollection{
        items: []interface{}{"Golang", "Python", "Java"},
    }

    iterator := collection.CreateIterator()
    for iterator.HasNext() {
        item := iterator.Next()
        fmt.Println(item)
    }
}

Chạy mã trên sẽ tạo ra đầu ra sau:

Golang
Python
Java

Trong mã trên, chúng ta xác định một lớp ConcreteCollection triển khai giao diện Collection, với phương thức CreateIterator() trả về một đối tượng trình lặp. Chúng ta sử dụng đối tượng trình lặp này trong hàm main() để duyệt.

4.4 Mở rộng bước triển khai: Đơn giản hóa việc triển khai Iterator bằng cách sử dụng Generator Function

Trong Golang, chúng ta có thể đơn giản hóa việc triển khai iterators bằng cách sử dụng generator function (yield). Dưới đây là một ví dụ về việc sử dụng generator function:

func GenerateItems() <-chan interface{} {
    items := []interface{}{"Golang", "Python", "Java"}

    out := make(chan interface{})
    go func() {
        defer close(out)
        for _, item := range items {
            out <- item
        }
    }()
    return out
}

func main() {
    for item := range GenerateItems() {
        fmt.Println(item)
    }
}

Trong đoạn mã trên, chúng ta xác định một hàm GenerateItems() trả về một channel chỉ đọc (<-chan), và sử dụng yield để gửi tuần tự các phần tử đến channel trong hàm này. Trong hàm main(), chúng ta sử dụng range để duyệt qua channel chỉ đọc này và đầu ra các giá trị phần tử.

Như vậy, chúng ta đã đơn giản hóa việc triển khai iterator bằng cách sử dụng generator function.