Паттерн Builder в Go
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 80 из 107.
Паттерн Builder отделяет создание сложного объекта от его представления. В отличие от паттерна Functional Options, Builder использует специальную структуру builder с методами, которые настраивают объект шаг за шагом.
Вот как реализовать builder для HTTP-запроса:
type Request struct {
method string
url string
headers map[string]string
body string
}
type RequestBuilder struct {
request *Request
}
func NewRequestBuilder() *RequestBuilder {
return &RequestBuilder{
request: &Request{
headers: make(map[string]string),
},
}
}
func (b *RequestBuilder) Method(m string) *RequestBuilder {
b.request.method = m
return b
}
func (b *RequestBuilder) URL(u string) *RequestBuilder {
b.request.url = u
return b
}
func (b *RequestBuilder) Header(key, value string) *RequestBuilder {
b.request.headers[key] = value
return b
}
func (b *RequestBuilder) Build() *Request {
return b.request
}Each method возвращает builder pointer, обеспечивая chaining method. Build() method завершает работу и возвращает constructed объект:
req := NewRequestBuilder().
Method("POST").
URL("https://api.example.com").
Header("Content-Type", "application/json").
Build()Паттерн Builder особенно эффективен, когда объектам требуется несколько шагов настройки или когда нужно обеспечить определённую последовательность конструирования. Он особенно полезен для создания неизменяемых объектов или когда сам процесс конструирования требует проверки.
Задание
ЛегкоДавайте создадим компоновщик сообщений электронной почты с использованием паттерна Builder! Сообщения электронной почты имеют множество необязательных компонентов — получателей, CC, темы, тела и вложений, — что делает их идеальным кандидатом для пошагового создания с помощью chaining методов.
Вы организуете код в двух файлах:
email.go: Define структуру электронной почты и её builder.Create структуру
Emailсо следующими полями:from(string)to(срез строк)cc(срез строк)subject(string)body(string)
Create структуру
EmailBuilder, которая holds pointer на создаваемый Email. ImplementNewEmailBuilder(), которая возвращает новый builder с initialized Email (empty срезами дляtoиcc).Add следующие методы builder, каждый из которых возвращает
*EmailBuilderдля chaining:From(address string)— устанавливает отправителяTo(address string)— adds получателя в срезtoCC(address string)— adds получателя в срезccSubject(subject string)— устанавливает строку темыBody(body string)— устанавливает тело электронной почты
Add метод
Build(), который возвращает constructed*Email.Finally, add метод
Summary()для Email, который возвращает строку в следующем формате:From: [from] To: [comma-separated to addresses] CC: [comma-separated cc addresses or "none"] Subject: [subject] Body: [body]main.go: Build и отображает сообщения электронной почты с использованием builder.Прочитайте адрес отправителя, затем число получателей и адрес каждого получателя. Далее прочитайте число CC-получателей и адрес каждого CC-получателя. Finally, прочитайте тему и тело.
Используйте EmailBuilder с chaining методов, чтобы construct электронное письмо, затем выведите его сводку.
Будут предоставлены следующие входные данные:
- Строка 1: адрес отправителя
- Строка 2: number получателей
- Следующие строки: адрес каждого получателя
- Следующая строка: number CC-получателей
- Следующие строки: адрес каждого CC-получателя
- Следующая строка: тема
- Последняя строка: тело
Например, если даны:
alice@company.com
2
bob@company.com
carol@company.com
1
manager@company.com
Weekly Report
Please find the weekly report attached.Ваш вывод должен быть таким:
From: alice@company.com
To: bob@company.com, carol@company.com
CC: manager@company.com
Subject: Weekly Report
Body: Please find the weekly report attached.А если даны:
support@service.com
1
customer@email.com
0
Your Ticket Update
Your support ticket has been resolved.Ваш вывод должен быть таким:
From: support@service.com
To: customer@email.com
CC: none
Subject: Your Ticket Update
Body: Your support ticket has been resolved.Обратите внимание, как builder позволяет добавлять несколько получателей по одному с помощью повторяющихся вызовов To(), а также как этот паттерн сохраняет процесс construct чистым и понятным даже при наличии множества необязательных полей.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Читаем адрес отправителя
from, _ := reader.ReadString('\n')
from = strings.TrimSpace(from)
// Читаем количество получателей
toCountStr, _ := reader.ReadString('\n')
toCount, _ := strconv.Atoi(strings.TrimSpace(toCountStr))
// Читаем адрес каждого получателя
toAddresses := make([]string, toCount)
for i := 0; i < toCount; i++ {
addr, _ := reader.ReadString('\n')
toAddresses[i] = strings.TrimSpace(addr)
}
// Читаем количество получателей CC
ccCountStr, _ := reader.ReadString('\n')
ccCount, _ := strconv.Atoi(strings.TrimSpace(ccCountStr))
// Читаем каждый адрес CC
ccAddresses := make([]string, ccCount)
for i := 0; i < ccCount; i++ {
addr, _ := reader.ReadString('\n')
ccAddresses[i] = strings.TrimSpace(addr)
}
// Читаем тему
subject, _ := reader.ReadString('\n')
subject = strings.TrimSpace(subject)
// Читаем тело письма
body, _ := reader.ReadString('\n')
body = strings.TrimSpace(body)
// TODO: Используйте EmailBuilder с цепочкой методов для создания письма
// Начните с NewEmailBuilder(), затем вызовите цепочку From(), To(), CC(), Subject(), Body()
// В конце вызовите Build(), чтобы получить Email
// TODO: Выведите сводку письма с помощью метода Summary()
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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Цепочки вызовов методовПсевдонимы и определения типовОсновы рефлексииОбзор генерации кодаПотренируйтесь самостоятельно: Онлайн-компилятор Go