Menu
Coddy logo textTech

Цепочки вызовов методов

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 81 из 107.

Цепочка вызовов методов (Method chaining) — это техника, при которой методы возвращают объект-получатель, что позволяет объединять несколько вызовов методов в одну инструкцию. Вы уже видели это в паттерне «Строитель» (Builder), но это самостоятельная концепция, полезная во многих контекстах.

Ключевым моментом является возврат указателя на структуру из каждого метода:

type Query struct {
    table   string
    columns []string
    where   string
    limit   int
}

func (q *Query) Select(cols ...string) *Query {
    q.columns = cols
    return q
}

func (q *Query) From(table string) *Query {
    q.table = table
    return q
}

func (q *Query) Where(condition string) *Query {
    q.where = condition
    return q
}

func (q *Query) Limit(n int) *Query {
    q.limit = n
    return q
}

Теперь вы можете вызывать эти методы по цепочке:

query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)

Это читается почти как естественный язык по сравнению с отдельными инструкциями. Цепочка вызовов методов работает лучше всего, когда операции настраивают или изменяют объект без необходимости возвращать другие значения. Она часто используется в построителях запросов, API конфигурации и библиотеках утверждений для тестов.

Помните, что каждый метод должен использовать указатель в качестве получателя (pointer receiver) и возвращать *StructType для правильной работы цепочки вызовов.

challenge icon

Задание

Легко

Давайте создадим гибкий построитель HTML-элементов, который использует цепочку вызовов методов (method chaining) для создания HTML-тегов с атрибутами и содержимым! Это практическое применение цепочки вызовов — вы создадите API, который при создании HTML-элементов читается почти как естественный язык.

Вы организуете свой код в двух файлах:

  • element.go: Определите ваш построитель HTML-элементов с методами, поддерживающими цепочку вызовов.

    Создайте структуру Element, которая хранит имя тега, карту атрибутов (строка к строке) и внутреннее содержимое. Создайте следующие методы для цепочки вызовов, каждый из которых возвращает *Element:

    • NewElement(tag string) — конструктор, который создает новый Element с заданным именем тега и инициализированной (пустой) картой атрибутов
    • Attr(key, value string) — добавляет атрибут к элементу
    • ID(id string) — удобный метод, который устанавливает атрибут "id"
    • Class(class string) — удобный метод, который устанавливает атрибут "class"
    • Content(text string) — устанавливает внутреннее содержимое элемента

    Добавьте метод Render(), который возвращает HTML-строку. Атрибуты должны располагаться в алфавитном порядке по ключу. Формат следующий:

    <tag attr1="value1" attr2="value2">content</tag>

    Если атрибутов нет, просто выведите <tag>content</tag>. Если содержимого нет, выведите <tag attr="value"></tag>.

  • main.go: Создайте HTML-элементы, используя цепочку вызовов методов.

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

    • id, за которым следует значение ID
    • class, за которым следует значение класса
    • attr, за которым следует имя атрибута и его значение (две строки)
    • content, за которым следует текст содержимого

    Соедините все операции в цепочку, а затем выведите готовый HTML-код.

Будут предоставлены следующие входные данные:

  • Строка 1: Имя тега
  • Строка 2: Количество операций
  • Следующие строки: Тип операции и значение(я)

Например, для входных данных:

div
3
id
container
class
main-content
content
Hello World

Ваш вывод должен быть:

<div class="main-content" id="container">Hello World</div>

И для входных данных:

a
3
attr
href
https://example.com
attr
target
_blank
content
Click here

Ваш вывод должен быть:

<a href="https://example.com" target="_blank">Click here</a>

И для входных данных:

input
2
attr
type
text
attr
placeholder
Enter name

Ваш вывод должен быть:

<input placeholder="Enter name" type="text"></input>

И для входных данных:

p
1
content
Simple paragraph

Ваш вывод должен быть:

<p>Simple paragraph</p>

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

Шпаргалка

Цепочка вызовов методов (method chaining) — это техника, при которой методы возвращают получателя (обычно *StructType), что позволяет связывать несколько вызовов методов в одну инструкцию.

Каждый метод должен использовать указатель на получателя и возвращать *StructType:

type Query struct {
    table   string
    columns []string
    where   string
    limit   int
}

func (q *Query) Select(cols ...string) *Query {
    q.columns = cols
    return q
}

func (q *Query) From(table string) *Query {
    q.table = table
    return q
}

func (q *Query) Where(condition string) *Query {
    q.where = condition
    return q
}

func (q *Query) Limit(n int) *Query {
    q.limit = n
    return q
}

Вызывайте методы по цепочке в свободном стиле:

query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)

Цепочка вызовов методов лучше всего подходит для случаев, когда операции настраивают или изменяют объект без необходимости возвращать другие значения. Она часто используется в построителях запросов (query builders), API конфигурации и библиотеках для тестовых утверждений.

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Чтение имени тега
	scanner.Scan()
	tagName := scanner.Text()

	// Чтение количества операций
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// Создание элемента
	element := NewElement(tagName)

	// Обработка каждой операции
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		operation := scanner.Text()

		// TODO: В зависимости от типа операции, вызовите соответствующий метод по цепочке
		// Операции могут быть: "id", "class", "attr" или "content"
		// - Для "id": прочитать следующую строку как значение ID
		// - Для "class": прочитать следующую строку как значение класса
		// - Для "attr": прочитать следующие ДВЕ строки (имя атрибута, затем значение)
		// - Для "content": прочитать следующую строку как текстовое содержимое
		// Используйте цепочку методов для гибкого построения элемента

		_ = operation // Удалите эту строку при реализации
	}

	// Вывод отрендеренного HTML
	fmt.Println(element.Render())
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование