Menu
Coddy logo textTech

Паттерн Builder в Go

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

Паттерн Строитель (Builder Pattern) отделяет конструирование сложного объекта от его представления. В отличие от паттерна Функциональные опции (Functional Options), Строитель использует специальную структуру строителя с методами, которые настраивают объект шаг за шагом.

Вот как реализовать строитель для 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
}

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

req := NewRequestBuilder().
    Method("POST").
    URL("https://api.example.com").
    Header("Content-Type", "application/json").
    Build()

Паттерн Строитель (Builder) отлично подходит в тех случаях, когда объекты требуют нескольких шагов настройки или когда вы хотите обеспечить определенную последовательность конструирования. Он особенно полезен для создания неизменяемых объектов или когда сам процесс конструирования требует валидации.

challenge icon

Задание

Легко

Давайте создадим компоновщик сообщений электронной почты, используя паттерн Строитель (Builder Pattern)! Сообщения электронной почты имеют множество необязательных компонентов — получатели, копии (CC), тема, тело письма, вложения — что делает их идеальным кандидатом для пошагового конструирования с использованием цепочки методов (method chaining).

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

  • email.go: Определите структуру электронного письма и его строителя.

    Создайте структуру Email со следующими полями:

    • from (string)
    • to (срез строк)
    • cc (срез строк)
    • subject (string)
    • body (string)

    Создайте структуру EmailBuilder, которая содержит указатель на конструируемый объект Email. Реализуйте функцию NewEmailBuilder(), которая возвращает новый строитель с инициализированным объектом Email (пустые срезы для to и cc).

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

    • From(address string) - устанавливает отправителя
    • To(address string) - добавляет получателя в срез to
    • CC(address string) - добавляет получателя в срез cc
    • Subject(subject string) - устанавливает тему письма
    • Body(body string) - устанавливает тело письма

    Добавьте метод Build(), который возвращает сконструированный *Email.

    Наконец, добавьте метод Summary() для структуры Email, который возвращает строку в следующем формате:

    From: [from]
    To: [comma-separated to addresses]
    CC: [comma-separated cc addresses or "none"]
    Subject: [subject]
    Body: [body]
  • main.go: Создавайте и отображайте сообщения электронной почты с помощью строителя.

    Считайте адрес отправителя, затем считайте количество получателей и каждый адрес получателя. Далее считайте количество получателей копии (CC) и каждый адрес CC. Наконец, считайте тему и тело письма.

    Используйте EmailBuilder с цепочкой методов для создания электронного письма, а затем выведите его сводку (summary).

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

  • Строка 1: Адрес отправителя
  • Строка 2: Количество получателей
  • Следующие строки: Каждый адрес получателя
  • Следующая строка: Количество получателей копии (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.

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

Шпаргалка

Паттерн Строитель (Builder Pattern) отделяет конструирование сложного объекта от его представления, используя специальную структуру-строитель с методами, которые настраивают объект шаг за шагом.

Базовая структура:

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),
        },
    }
}

Методы строителя возвращают указатель на строитель, чтобы обеспечить цепочку вызовов (method chaining):

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
}

Метод Build() завершает процесс и возвращает сконструированный объект:

func (b *RequestBuilder) Build() *Request {
    return b.request
}

Использование с цепочкой вызовов:

req := NewRequestBuilder().
    Method("POST").
    URL("https://api.example.com").
    Header("Content-Type", "application/json").
    Build()

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

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

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()
}
quiz iconПроверьте себя

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

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