Шаблон итератора шаблона проектирования golang

Go Шаблоны проектирования

шаблон итератора

определение

wiki: В объектно-ориентированном программировании шаблон итератора — это шаблон проектирования, самый простой и наиболее распространенный шаблон проектирования. Это позволяет пользователям посещать каждый элемент в контейнере через определенный интерфейс, не зная базовой реализации.

Проще говоря, установив функцию итератора для контейнера, вы можете использовать эту итеративную функцию для последовательного доступа к каждому элементу в нем, при этом внешнему миру не нужно знать базовую реализацию.

Если рекомбинироватьшаблон посетителя, передайте ему пользовательского посетителя, тогда вы сможете разрешить посетителю доступ к каждому элементу в контейнере.

Диаграмма классов

(Фиговое исходное сеть)

Роль

  • Абстрактный агрегатный класс: определение абстрактного контейнера

  • Конкретный класс агрегации: реализуйте вышеуказанный абстрактный класс в качестве контейнера для хранения элементов и ожидания итерации.

  • Абстрактный итератор: интерфейс итератора, каждый контейнер имеет конкретную реализацию интерфейса итератора.

  • Конкретные итераторы: в соответствии с разными контейнерами необходимо определить разные конкретные итераторы, а также определить конкретную реализацию движения курсора.

взять каштан

  1. Создайте абстрактную структуру контейнера
 // 容器接口
 type IAggregate interface {
 	Iterator() IIterator
 }
  1. Создайте абстрактный итератор
 // 迭代器接口
 type IIterator interface {
     HasNext() bool
     Current() int
     Next() bool
 }

Основные требования к итераторам требуют наличия метода, определяющего, следует ли выполнять итерацию до конца.HasNext(), должен быть способ получить текущий элементCurrent(), должен быть способ переместить курсор к следующему элементуNext()

  1. реализовать контейнер
  // 具体容器
  type Aggregate struct {
    container []int // 容器中装载 int 型容器
  }
  // 创建一个迭代器,并让迭代器中的容器指针指向当前对象
  func (a *Aggregate) Iterator() IIterator {
       i := new(Iterator)
       i.aggregate = a
       return i
   }

Для простоты здесь мы просто позволяем контейнеру хранитьintтип данных

  1. реализовать итератор
 type Iterator struct {
     cursor    int // 当前游标
     aggregate *Aggregate // 对应的容器指针
 }
 
 // 判断是否迭代到最后,如果没有,则返回true
 func (i *Iterator) HasNext() bool {
     if i.cursor+1 < len(i.aggregate.container) {
         return true
     }
     return false
 }
 
 // 获取当前迭代元素(从容器中取出当前游标对应的元素)
 func (i *Iterator) Current() int {
     return i.aggregate.container[i.cursor]
 }
 
 // 将游标指向下一个元素
 func (i *Iterator) Next() bool {
     if i.cursor < len(i.aggregate.container) {
         i.cursor++
         return true
     }
     return false
 }
  1. использовать итератор
 func main() {
     // 创建容器,并放入初始化数据
     c := &Aggregate{container: []int{1, 2, 3, 4}}
     // 获取迭代器
     iterator := c.Iterator() 
     for {
     	// 打印当前数据
 	    fmt.Println(iterator.Current())
 	    // 如果有下一个元素,则将游标移动到下一个元素
 	    // 否则跳出循环,迭代结束
 	    if iterator.HasNext() {
 		    iterator.Next()
 	    } else {
 		    break
 	    }
     }
 }

Приведенный выше пример относительно прост, вы можете себе представить, если тип контейнераcontainerНе срез или массив, а массив структур, и нам просто нужно перебрать поле в каждой структуре? Поле структуры представляет собой массив, или поле, которое необходимо итерировать в этой структуре, является приватным и не может быть доступно вне пакета, поэтому в настоящее время, используя итератор, вы можете настроить итеративную функцию, выставляя только мало для внешнего мира.Метод для получения данных, и внешнему все равно, является ли контейнер структурой или массивом.

настоящие каштаны

Возможно, мне снова придется писать свои собственные колеса...

Но здесь я говорю только о практическом применении моего шаблона итератора,Это колесо еще не закончено, это просто полуфабрикат

GitHub.com/Четыре потока/Бин…

Это код, который я планирую написать без использования стандартной библиотеки.templateРеализованный шаблонизатор позже заглох из-за загруженности работой, требовалось знание принципа компиляции, на данный момент написан только лексический анализ...

возможно придется подождатьBingoПосле того как все модули написаны, приступим к реализации...

Немного бреда... к делу...

Механизм шаблонов должен разделить символы в шаблоне на лексические цепочки в соответствии с левым и правым разделителями тега шаблона, например, в следующем шаблоне:

 {{ for item in navigation }}
     <li>tag</li>
 {{ endfor }}

Этот шаблон означает пересечениеnavigation, распечатайте соответствующее количествоliЭтикетка

создаст следующую лексическую цепочку

  [for]-> [item]-> [in]-> [navigation]-> [<li>tag</li>]-> [endfor] 

Каждая квадратная скобка представляет узел в лексической цепочке, и каждый узел различает, является ли он текстовым узлом или узлом грамматики.

Конкретный способ реализации здесь не упоминается.Он предполагает переход состояния лексического анализатора.Если интересно,можете поискать самостоятельно.Далее процесс печати лексической цепочки при отладке,используя режим итератора.

Структура лексической цепочки следующая:

 // 词法分析链包含大量节点
 type LexicalChain struct {
 	Nodes       []*LexicalNode     
 	current     int                    // 当前指针
 	Params      map[string]interface{} // 变量名->变量值
 	TokenStream *TokenStream           // token流,这是通过节点解析出来的
 }

Структура соответствующего лексического узла следующая:

 type LexicalNode struct {
 	T       int      // 类型(词法节点还是文本节点)
 	Content []byte   // 内容,生成模版的时候要使用内容进行输出
 	tokens  []*Token // token流
 	root    *Token   // 抽象语法树跟节点
 	lineNum int      // 行数
 	stack   []int    // 符栈,用来记录成对的操作符
 }

метод печати для каждого узлаPrint():

 // 打印节点值
 func (n *LexicalNode) Print() {
 	switch n.T {
 	case textNode:
 		fmt.Println("[node type]: TEXT") // 文本节点
 	case lexicalNode:
 		fmt.Println("[node type]: LEXICAL") // 词法节点
 	default:
 		fmt.Println("[node type]: UNKNOWN TYPE") // 未知类型
 		break
 	}
 	fmt.Println("[line number]: " + strconv.Itoa(n.lineNum))
 	fmt.Println("[content]: " + string(n.Content))
 }

Вышеупомянутое предназначено для печати узла.Когда вся лексическая цепочка должна быть напечатана, вся лексическая цепочка должна быть повторена, и каждый узел вызываетсяPrint()метод:

  func (l *LexicalChain) Print() {
  	// 打印当前节点
  	l.Iterator(func(node *LexicalNode) {
  		fmt.Println("====================")
  		fmt.Println("[index]: " + strconv.Itoa(l.current))
  		node.Print()
  	})
  }    

Метод реализации здесь отличается от первого каштана, его можно рассматривать как паттерн итератора ишаблон посетителяиспользовать в сочетании с методами итератораIterator(), передать функцию обратного вызова в качестве посетителя,

вызов для каждого узлаPrint()метод печати узлов.

Посмотрите нижеIterator()Реализация метода:

  func (l *LexicalChain) Iterator(call func(node *LexicalNode)) {
  	// 对于链中的每个节点,执行传入的方法
  	call(l.Current()) // 调用传入的方法,将当前节点作为参数传入
  	for {
  		if l.Next() != -1 {  // 这里和第一个栗子一样,将游标指向下一个元素,并继续调用 传入的回调
  			call(l.Current())
  		} else {
  			break  // 如果迭代到了最后,则直接跳出循环,结束迭代
  		}
  	}
  }

Next()Реализация функции аналогична первому каштану, поэтому выкладывать не буду, код вздесь

Таким образом, сгенерированную лексическую цепочку можно распечатать, что удобно для последующей отладки и разработки...

Суммировать

Шаблон итератора является самым простым и наиболее часто используемым поведенческим шаблоном.JAVAособенно распространен вphperесли ты привыкнешь к этомуlaravelизсобирать, вы можете ознакомиться сfilter/mapРеализация других методов реализуется путем передачи обратного вызова, а затем повторения элементов внутри метода для достижения фильтрации.

преимущество:

  1. Излишне говорить, что самое большое преимущество шаблона проектирования — развязка, добавление контейнеров или итераторов без модификации исходного кода и упрощение класса контейнера, извлечение логики итерации и размещение ее в итераторе.
  2. Вы можете проходить один и тот же объект по-разному (это то, что я сказал выше, передавая разные обратные вызовы для разных итераций). недостаток:
  3. Режим итераторов является разделением ответственности на хранение и обход ответственности объекта (контейнера). Как уже упоминалось в первом кашнере, добавление класса контейнеров требует добавления класса итератора, который увеличивает количество кода и сложности.

Вышеприведенный код помещается вgolang-design-patternsна этом складе

Рекламируйте и рекомендуйте фреймворк go web, написанный вамиbingo, просить звезду, просить пиар ~