Ответ
В Go я придерживаюсь идиоматического подхода, который ценит простоту, читаемость и явное управление зависимостями. Вместо классических GoF-паттернов, основанных на наследовании, в Go используются свои подходы.
Идиоматические паттерны Go
-
Интерфейсы для декомпозиции
- Это основной инструмент для создания абстракций и слабой связности (decoupling). В Go принято определять небольшие, сфокусированные интерфейсы (
io.Reader
,http.Handler
).// Позволяет любому типу, реализующему метод, быть использованным как хранилище type Storage interface { Get(id string) (Item, error) Save(item Item) error }
- Это основной инструмент для создания абстракций и слабой связности (decoupling). В Go принято определять небольшие, сфокусированные интерфейсы (
-
Композиция вместо наследования
- Встраивание (embedding) структур позволяет переиспользовать функциональность без создания сложных иерархий наследования.
type Server struct { *http.Server // Встраиваем стандартный http.Server db Storage logger *log.Logger }
- Встраивание (embedding) структур позволяет переиспользовать функциональность без создания сложных иерархий наследования.
-
Функциональные опции (Functional Options)
- Паттерн для гибкой и расширяемой конфигурации объектов. Позволяет создавать конструкторы с множеством необязательных параметров.
type Client struct { timeout time.Duration retries int }
func NewClient(opts ...func(Client)) Client { c := &Client{timeout: 10 * time.Second, retries: 3} // значения по умолчанию for _, opt := range opts { opt(c) } return c }
func WithTimeout(t time.Duration) func(Client) { return func(c Client) { c.timeout = t } }
- Паттерн для гибкой и расширяемой конфигурации объектов. Позволяет создавать конструкторы с множеством необязательных параметров.
-
Паттерн "Декоратор"
- Часто используется для реализации middleware в HTTP-серверах, добавляя сквозную функциональность (логирование, метрики, аутентификация) к обработчикам.
Архитектурные подходы
- Dependency Injection (DI): Зависимости (например, подключение к БД, логгер) передаются в компоненты явно через конструкторы или фабричные функции, обычно в виде интерфейсов. Это упрощает тестирование и замену компонентов.
- Чистая архитектура (Clean Architecture) / Гексагональная архитектура: Я стараюсь разделять приложение на слои с четкими границами:
- Domain/Entities: Основная бизнес-логика, независимая от всего.
- Use Cases/Services: Оркестрация бизнес-логики.
- Interfaces/Adapters: Контроллеры, репозитории, шлюзы к внешним системам.
- Frameworks/Drivers: Веб-сервер, БД, брокеры сообщений.
Главный принцип — писать простой, поддерживаемый код, следуя принципам SOLID там, где это оправдано и не усложняет решение.