1. Что такое шаблон Iterator
Шаблон итератор - это поведенческий шаблон проектирования, который предоставляет унифицированный метод обхода элементов агрегатного объекта без раскрытия внутреннего представления этого объекта.
2. Характеристики и преимущества шаблона Iterator
Характеристики и преимущества шаблона итератора следующие:
- Он может скрывать внутреннюю структуру объекта коллекции, отделяя алгоритм обхода от объекта коллекции.
- Предоставляет стандартизированный способ обхода различных типов объектов коллекций.
- Упрощает клиентский код, делая его более четким и кратким.
- Может предоставлять различные реализации итераторов для адаптации к различным потребностям обхода.
3. Примеры практического применения шаблона Iterator
Шаблон итератора имеет множество практических применений, таких как:
- Обход результирующего набора запроса к базе данных.
- Обход файлов и папок в файловой системе.
- Обход элементов в коллекции.
4. Внедрение шаблона Iterator на Golang
4.1 UML-диаграмма классов
4.2 Введение в пример
На UML-диаграмме классов выше у нас есть две основные роли: Iterator и Collection.
- Итератор определяет интерфейс для обхода объекта коллекции, включая метод
HasNext()
для определения наличия следующего элемента и методNext()
для получения следующего элемента. - ConcreteIterator - это конкретный класс реализации Iterator, реализующий методы
HasNext()
иNext()
. - Коллекция определяет интерфейс для создания объектов итераторов с методом
CreateIterator()
. - ConcreteCollection - это конкретный класс реализации Collection, реализующий метод
CreateIterator()
.
4.3 Шаги реализации
Затем мы реализуем шаблон Iterator на Golang поэтапно.
4.3.1 Определение интерфейса Iterator и класса Concrete 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 Определение интерфейса объекта, который можно перебирать, и конкретного класса объекта, который можно перебирать
type Collection interface {
CreateIterator() Iterator
}
type ConcreteCollection struct {
items []interface{}
}
func (c *ConcreteCollection) CreateIterator() Iterator {
return &ConcreteIterator{
collection: c,
index: 0
}
}
4.3.3 Реализация логики генерации итератора в классе объекта, который можно перебирать
func main(){
collection := &ConcreteCollection{
items: []interface{}{"Golang", "Python", "Java"},
}
iterator := collection.CreateIterator()
for iterator.HasNext() {
item := iterator.Next()
fmt.Println(item)
}
}
Запуск приведенного выше кода приведет к следующему выводу:
Golang
Python
Java
В приведенном выше коде мы определяем класс ConcreteCollection
, реализующий интерфейс Collection
, с его методом CreateIterator()
, возвращающим объект итератора. Мы используем этот объект итератора в функции main()
для обхода.
4.4 Расширение реализации шага: упрощение реализации итератора с использованием генераторной функции
В Golang мы можем упростить реализацию итераторов, используя генераторную функцию (yield
). Вот пример использования генераторной функции:
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)
}
}
В приведенном выше коде мы определяем функцию GenerateItems()
, возвращающую только для чтения канал (<-chan
), и используем yield
для последовательной отправки элементов в этот канал внутри этой функции. В функции main()
мы используем range
для обхода этого только для чтения канала и вывода значений элементов.
Таким образом, мы упростили реализацию итератора, используя генераторную функцию.