Расскажите о паттерне «Цепочка обязанностей» (Chain of Responsibility) и приведите пример его использования в Go.

Ответ

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

Самый частый и идиоматичный пример использования этого паттерна в Go — это HTTP middleware.

Пример: Middleware для HTTP-сервера

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

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

// Middleware для логирования запросов
func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        // Передаем управление следующему обработчику в цепочке
        next.ServeHTTP(w, r)
        log.Printf("Completed in %v", time.Since(start))
    })
}

// Middleware для проверки "аутентификации"
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Header.Get("X-API-KEY") != "secret" {
            http.Error(w, "Forbidden", http.StatusForbidden)
            return // Прерываем цепочку, если аутентификация не пройдена
        }
        // Передаем управление дальше
        next.ServeHTTP(w, r)
    })
}

// Конечный обработчик запроса
func finalHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
}

func main() {
    final := http.HandlerFunc(finalHandler)
    // Собираем цепочку: сначала логирование, потом аутентификация
    chain := LoggingMiddleware(AuthMiddleware(final))

    http.Handle("/", chain)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Плюсы:

  • Гибкость: Позволяет динамически добавлять, удалять и изменять порядок обработчиков.
  • Разделение ответственности: Каждый обработчик сфокусирован на одной задаче (логирование, аутентификация, кэширование и т.д.).
  • Слабая связность: Обработчик не знает о структуре цепочки или о других обработчиках, он знает только о следующем звене.

Минусы:

  • Нет гарантии обработки: Запрос может пройти всю цепочку и остаться необработанным.
  • Сложность отладки: Может быть трудно отследить, какой именно обработчик в цепочке вызвал ошибку или повел себя некорректно.

Ответ 18+ 🔞

А, паттерн Chain of Responsibility! Ну это ж как в жизни, блядь: приходишь ты в какую-нибудь контору, а там тебя по кабинетам гоняют — один говорит "не моё", второй "не ко мне", третий "а вы справку взяли?", и в итоге ты с пустыми руками, сука, выходишь, и все довольны, кроме тебя, пиздец.

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

Самый, блядь, классический пример — это HTTP middleware в Go. Прям идиоматичная хуйня, все так делают.

Пример: Middleware для HTTP-сервера

Каждый middleware — это как раз тот самый мудак в цепочке. Он что-то своё делает (логирует, проверяет) и либо дальше передаёт, либо нахуй посылает.

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

// Middleware для логирования запросов
func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        // Передаем управление следующему обработчику в цепочке
        next.ServeHTTP(w, r)
        log.Printf("Completed in %v", time.Since(start))
    })
}

// Middleware для проверки "аутентификации"
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Header.Get("X-API-KEY") != "secret" {
            http.Error(w, "Forbidden", http.StatusForbidden)
            return // Прерываем цепочку, если аутентификация не пройдена
        }
        // Передаем управление дальше
        next.ServeHTTP(w, r)
    })
}

// Конечный обработчик запроса
func finalHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
}

func main() {
    final := http.HandlerFunc(finalHandler)
    // Собираем цепочку: сначала логирование, потом аутентификация
    chain := LoggingMiddleware(AuthMiddleware(final))

    http.Handle("/", chain)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Плюсы, ёпта:

  • Гибкость: Хочешь — добавь нового мудака в цепочку, хочешь — выкини. Порядок менять — раз плюнуть.
  • Разделение ответственности: Один логирует, другой проверяет, третий кеширует. Каждый сидит в своей параше и не лезет в чужую, красота.
  • Слабая связность: Каждый обработчик знает только про следующего идиота в цепочке, а не про всю систему. Это как в тюрьме — знаешь только своего "соседа".

Минусы, блядь:

  • Нет гарантии обработки: Запрос может прошататься по всей цепочке, как последний лох, и так нигде и не зацепиться. Прошёл всех — и свободен, пиздец.
  • Сложность отладки: Попробуй найди, кто именно из этих уродов в цепочке всё сломал. Это как искать иголку в стоге сена, если в каждом стоге сидит по программисту.