【问题标题】:Golang Import Cycle ChallengeGolang 导入周期挑战
【发布时间】:2019-03-30 19:18:21
【问题描述】:

我的 go 代码中存在循环依赖问题(不允许导入循环),并且不确定解决它的最佳方法。我认为我对 golang 接口缺乏了解可能会影响我看到前进方向的能力。

我的问题 - 我有两个包:

  • 事件 - 事件主要是一个“父级”,并且会在锻炼包中进行多次调用
  • Workout - 在一种情况下,我需要调用 Event 包,这会产生循环依赖,因为 Event 已经消耗了 Workout

允许 Workout 在 Event 域中调用函数(而不是对象上的方法)的最佳方式是什么?

下面是我的简化代码,以帮助提供一些上下文

//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
    }

【问题讨论】:

  • 返回类型为*EventWorkout。如果它们紧密耦合,您不会考虑合并这些包吗?否则我会创建另一个包来定义将由 event 包中的结构实现的接口,并用它替换当前的返回类型。
  • @Berkant 你不需要第三个包来定义接口,因为接口只需要被消费包引用,而不是被实现它的包引用;此外,相同的接口可以在多个包中定义并且是等效的。

标签: go dependencies package


【解决方案1】:

您可以通过界面执行此操作。在事件包中,您需要给 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,因此有人可以直接进入锻炼(绕过事件)。我将它们作为单独的包保存,因为我希望以后能够轻松地分离和微服务它们。也许最好的方法是让 Event 的 init() 函数填充锻炼包中的 EventFinder 界面?不确定这是否适用于运行测试?
  • 如果有帮助,请用更多代码更新 OP,在这里非常感谢您的想法
  • 嗯,那就更棘手了,事件包上的 init 函数只会在调用事件时被调用,所以如果第一次调用你的系统进入锻炼,那么将不会触发设置该变量。另一种选择是拥有一个事件和锻炼都依赖的第三个包,它只包含 GetEventByDate 函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-21
  • 2018-01-21
  • 1970-01-01
相关资源
最近更新 更多