[ 1 ] Что в языке считается наиболее правильным и адекватным способом управления структурой логики проекта в плане соотношения ООП и функциональной составляющей?
В качестве пет-проекта пишу MVC-фреймворк. Представим, что мне нужно сделать контроллер.
Я вижу два способа:
1) Контроллера как такового нет, а хендлеры --- это публичные функции пакета.
Все зависимости (бд, сервисы, DAO) передаются в функцию пакета, а она возвращает функцию сигнатуры http.HandleFunc;
2) Контроллер --- это структура с методами, соответствующими сигнатуре http.HandleFunc. Зависимости передаются в структуру при инициализации.
[ 2 ] Как предпочтительнее комбинировать объекты и функции?
Представим, что есть структура Worker с определенным набором данных и методом Run().
[ 3 ] Если функции, вызываемые в Run(), используют данные объекта, их следует писать как методы воркера или как функции пакета?
[ 4 ] А что, если функция требует *sql.DB, который есть в воркере? Должен ли я писать эту функцию в виде метода или писать ее в виду функции пакета, принимающей в аргументы *sql.DB?
@eugene.a.nail относительно структура контроллера в MVC-фреймворка, то есть два подхода:
Контроллер как таковой отсутствует, и обработчики являются публичными функциями пакета. Зависимости (например, база данных, сервисы, DAO) передаются в функцию пакета, а она возвращает функцию с сигнатурой http.HandleFunc
.
1 2 3 4 5 |
func NewHandler(db *sql.DB, service *Service) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Обработка запроса с использованием db и service } } |
Контроллер представляет собой структуру с методами, которые соответствуют сигнатуре http.HandleFunc
. Зависимости передаются в структуру при инициализации.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
type Controller struct { db *sql.DB service *Service } func NewController(db *sql.DB, service *Service) *Controller { return &Controller{db: db, service: service} } func (c *Controller) Handler(w http.ResponseWriter, r *http.Request) { // Обработка запроса с использованием c.db и c.service } |
относительно работы с *sql.DB и если функция требует *sql.DB, который есть в Worker
, то тут возможны оба подхода:
Если функция логически связана с объектом и часто используется в его контексте, можно сделать её методом.
1 2 3 4 5 6 7 8 |
type Worker struct { db *sql.DB // другие поля } func (w *Worker) QueryData() { // Использование w.db } |
Если функция более универсальна и может быть полезна в других контекстах, лучше сделать её функцией пакета, принимающей *sql.DB
.
1 2 3 |
func QueryData(db *sql.DB) { // Использование db } |