Цепочки вызовов методов
Часть раздела Объектно-ориентированное программирование путешествия по 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 для правильной работы цепочки вызовов.
Задание
ЛегкоДавайте создадим гибкий построитель 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, за которым следует значение IDclass, за которым следует значение класса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())
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool12Продвинутые концепции ООП
Паттерн функциональных опцийПаттерн Builder в GoЦепочки вызовов методовПсевдонимы и определения типовОсновы рефлексииОбзор генерации кода