У меня проблема с циклической зависимостью (цикл импорта не разрешен) в моем коде go, и я не уверен, как лучше всего ее решить. Я думаю, что мое непонимание интерфейсов golang может повлиять на мою способность видеть путь вперед.
Моя проблема - у меня два пакета:
Как лучше всего разрешить Workout вызывать функцию (а не метод объекта) в домене событий?
Ниже мой упрощенный код, чтобы помочь предоставить некоторый контекст.
//workout package
//This is a private function on the workout package that is
//attempting to call a public function on the Event Package
func findWorkoutAssociatedToActivityTcx(txcObject *DataTcx) *EventWorkout{
return event.GetEventByDate(txcObject.ActivityDate, "", "")
}
Можно ли создать интерфейс для этой функции? Я не совсем понимаю, как бы я это сделал. Большое тебе спасибо.
// ОБНОВЛЕНИЯ - Код при попытке настроить интерфейс
//WorkoutPackage
//workout package
//This is a private function on the workout package that is
//attempting to call a public function on the Event Package
func findWorkoutAssociatedToActivityTcx(txcObject *DataTcx, userID, transactionID string) *Workout {
//return event.GetEventByDate(txcObject.ActivityDate, "", "")
MyEventFinder.GetEventByDate(txcObject.ActivityDate, userID, transactionID)
return nil
}
var MyEventFinder EventFinder
type EventFinder interface {
GetEventByDate(time.Time, string, string) (*sharedstructs.ListOfEvents, error)
}
// Пакет событий
type eventProvider struct{}
func (e eventProvider) GetEventByDate(date time.Time, userID, transactionID string) (*sharedstructs.ListOfEvents, error) {
redFalconLogger.LogDebug("event.GetEventByDate: ", transactionID)
if date.IsZero() || userID == "" {
return nil, sharedstructs.InvalidData{Msg: "Invalid date or userID"}
}
//Create the query params
queryParamArray, queryParamCreationError := createQueryParamForQueryByDate(&date, &userID)
if queryParamCreationError != nil {
return nil, queryParamCreationError
}
//perform the query - pass empty orderBy because I don't care
queryResults, queryError := queryForEvent(*queryParamArray, "")
if queryError != nil {
switch queryError.(type) {
case firestorehelper.UnqueryableCollection:
return nil, sharedstructs.Forbidden{Msg: "operation is forbidden, probably due to malformed query"}
default:
return nil, sharedstructs.InternalServerError{Msg: "something went wrong in the query"}
}
}
return queryResults, nil
}
@Berkant вам не нужен третий пакет для определения интерфейсов, потому что на интерфейс нужно ссылаться только у потребляющих пакетов, а не у пакетов, реализующих его; и, кроме того, идентичные интерфейсы могут быть определены в нескольких пакетах и будут эквивалентными.
Вы уверены, что можете сделать это с помощью интерфейса. В пакете событий необходимо передать GetEventByDate структуру получателя:
type eventProvider struct {}
func (e eventProvider) GetEventByDate(t time.Time, a, b string) *workout.EventWorkout{...}
Тогда в пакете тренировок:
type EventFinder interface {
GetEventByDate(time.Time, string, string) *EventWorkout
}
Затем вы можете передать экземпляр eventProvider
в пакет тренировки и использовать его только через интерфейс EventFinder
, который не зависит от времени компиляции пакета event
. Вы не показываете, как вы вызываете пакет workout
, но это может быть либо параметром вызова метода, либо его установкой при создании структуры в пакете тренировки.
Большое спасибо! Это именно то, на что я надеялся. Думаю, я все еще немного не понимаю, как заполнить мой интерфейс в моем пакете тренировок. Должен ли я делать это в init () пакета событий? Будет ли это работать для тестирования? Я действительно пробовал что-то подобное и получил ошибку нулевой ссылки. Тем не менее, попробую то, что вы предлагаете. Спасибо
Как вы сейчас вызываете пакет тренировки? Я предположил, что все происходит из пакета событий, а затем перетекает в тренировку, если это так, просто создайте экземпляр eventProvider и передайте его, когда вы вызываете тренировку упаковка
По большей части это правда, но и событие, и тренировка имеют отдельные API поверх них, так что кто-то может перейти непосредственно к тренировке (минуя событие). Я сохранил их как отдельные пакеты, потому что хочу иметь возможность легко разделять и обрабатывать их в микросервисах позже. Может быть, лучше всего использовать функцию init () события Event для заполнения интерфейса EventFinder в пакете тренировки? Не уверены, что это сработает для запуска тестов?
Обновил OP с дополнительным кодом, если это помогает, искренне цените ваши мысли здесь
Хм, тогда это сложнее, функция init в пакете событий будет вызываться только тогда, когда что-то вызывает событие, поэтому, если первый вызов в вашей системе идет на тренировку, тогда не будет триггера для настройки этой переменной. Другой вариант - иметь третий пакет, на который полагаются как мероприятие, так и тренировка, который просто содержит функцию GetEventByDate.
Большое тебе спасибо. Это объясняет, почему у меня возникла проблема с работой интерфейса. Я ценю вашу помощь. Это многое прояснило.
Возвращаемый тип - * EventWorkout. Разве вы не подумали бы об объединении этих пакетов, если они тесно связаны? В противном случае я бы создал другой пакет, чтобы определить интерфейсы, которые будут реализованы структурами из пакета
event
, и заменить им текущий тип возвращаемого значения.