Ответ
В Go, благодаря его особенностям (интерфейсы, функции первого класса, горутины и каналы), классические паттерны GoF часто реализуются иначе или заменяются более идиоматичными подходами. Однако многие концепции остаются актуальными.
Классические паттерны
-
Фабричный метод (Factory Method)
- Описание: Создание объектов через специальную функцию, а не напрямую через конструктор. Это позволяет скрыть сложную логику инициализации.
- Пример:
// NewFileLogger и NewConsoleLogger — это фабричные функции func NewLogger(logType string) (Logger, error) { switch logType { case "file": return NewFileLogger("/var/log/app.log"), nil case "console": return NewConsoleLogger(), nil default: return nil, fmt.Errorf("unknown logger type: %s", logType) } }
-
Одиночка (Singleton)
- Описание: Гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В Go идиоматично реализуется с помощью
sync.Once
. - Пример:
type singleton struct{}
var ( instance *singleton once sync.Once )
func GetInstance() *singleton { once.Do(func() { instance = &singleton{} }) return instance }
- Описание: Гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В Go идиоматично реализуется с помощью
-
Декоратор (Decorator)
- Описание: Динамически добавляет объекту новую функциональность, «оборачивая» его. В Go легко реализуется через встраивание (embedding) интерфейсов.
- Пример: Логгирование запросов к HTTP-хендлеру.
func LoggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("Request: %s %s", r.Method, r.URL.Path) next.ServeHTTP(w, r) }) }
// Использование: http.Handle("/", LoggingMiddleware(myHandler))
-
Стратегия (Strategy)
- Описание: Определяет семейство схожих алгоритмов и помещает каждый из них в собственный класс, после чего алгоритмы можно взаимозаменять. В Go это достигается через интерфейсы.
- Пример:
type Sorter interface { Sort(data []int) }
type BubbleSort struct{} func (b BubbleSort) Sort(data []int) { / ... / }
type QuickSort struct{} func (q QuickSort) Sort(data []int) { / ... / }
// Контекст, который использует стратегию type Context struct { sorter Sorter }
func (c *Context) DoSort(data []int) { c.sorter.Sort(data) }
Go-идиоматичные паттерны
-
Наблюдатель (Observer) через каналы
- Описание: Вместо сложных интерфейсов и списков подписчиков, в Go для рассылки событий используются каналы. Это проще и безопаснее с точки зрения конкурентности.
- Пример:
// Издатель рассылает события в канал func publisher(ch chan<- string) { for i := 0; i < 5; i++ { ch <- fmt.Sprintf("Event %d", i) time.Sleep(time.Second) } close(ch) }
// Подписчик читает из канала func subscriber(ch <-chan string) { for event := range ch { log.Println("Received:", event) } }
-
Функциональные опции (Functional Options)
- Описание: Паттерн для создания сложных объектов с множеством необязательных параметров. Он более гибок, чем использование структуры с настройками.
- Пример:
type Server struct { Addr string Port int Timeout time.Duration }
type Option func(*Server)
func WithAddr(addr string) Option { return func(s *Server) { s.Addr = addr } }
func WithPort(port int) Option { return func(s *Server) { s.Port = port } }
func NewServer(opts ...Option) Server { // Значения по умолчанию srv := &Server{Addr: "localhost", Port: 8080, Timeout: time.Second 30} // Применяем опции for _, opt := range opts { opt(srv) } return srv }
// Использование: server := NewServer(WithPort(9000), WithAddr("0.0.0.0"))