如何解耦这种传递依赖

How to decouple this transitive dependency

我正在尝试找出如何从我的服务中删除传递依赖项。

让我们调用我的服务 ServiceA.

ServiceA 依赖于 LibraryB库 B 依赖于 库 C。因此 ServiceA 可传递地依赖于 LibraryC。让我解释一下如何...

在这种情况下,LibraryC 恰好是 ozzo-validation 库。在这个库中,有一个名为 Errors 的类型被定义为 map[string]error。您可以在 https://github.com/go-ozzo/ozzo-validation/blob/v3.6.0/error.go 看到它,但这里仅供参考:

package validation

type Errors map[string]error

// Implement the error interface
func (es Errors) Error() string {
    // Implementation omitted for brevity
}

注意类型 Errors 实现了 error 接口。

正如我已经写过的,LibraryB 依赖于 LibraryC,ozzo-validationLibraryBozzo-validation的使用是这样的:

package web

// Error responds to a request with an error object and the specified status
func Error(w http.ResponseWriter, err error, status int) {
    // Implementation omitted for brevity
    errors, ok := err.(validation.Errors)
    if ok {
        for key, err1 := range errors {
            // Implementation omitted for brevity
        }
        // Implementation omitted for brevity
    }
    // Implementation omitted for brevity
}

这就是全部用法。 LibraryB 导入 ozzo-validation 以便 LibraryB 可以进行类型断言,errors, ok := err.(validation.Errors),然后在地图上范围,for key, err1 := range errors.

我的服务 ServiceA 不知道 LibraryB 依赖于 ozzo-validation. ServiceA 也想使用 ozzo-validation,但需要使用较新的版本,因为较新的版本具有更多的功能和一些重要的错误修复。这个较新的版本是 v4.3.0。 ServiceA 调用 ozzo-library 中的一些方法 return 一个 validation.Errors 实例并将该实例传递给 LibraryBweb.Error 函数作为 err error 参数。

这就是乐趣的开始。因为 ServiceA 正在传递一个 v4.3.0 validation.Errors 实例并且 LibraryB 是针对 v3.6.0 validation.Errors 的类型断言即使 v3.6.0 和 v4.3.0 中的类型定义完全相同,类型断言也会失败。

我该如何解决这个问题?

我可以访问 LibraryB 的源代码并且我可以更改它。我可以轻松升级 LibraryB 以使用 ozzo-validation 的 v4.3.0,但这会使这种传递耦合永久化。我宁愿完全删除这种传递耦合。

我尝试将 LibraryB 中的类型断言更改为

errors, ok := err.(map[string]error)

因为最终实例就是这样,map[string]error但是编译器不喜欢那样,因为 map[string]error 没有实现 error 接口。

有什么方法可以让我自己的对象实现 error 并且也可以调整范围,这样我就可以将 v4.3.0 `validation.Errors 包装在某种接口或其他东西中那会打破这种传递耦合?

我该怎么做才能打破这种紧密的传递耦合?

如果 LibraryB 的向后兼容性是一个问题,那么将 LibraryB 中的 ozzo-validation 升级到 v4 不是一个选项。因为如果有一个ServiceD使用LibraryC@v3和LibraryB,这样的升级会破坏ServiceD。

幸运的是,在 Go Modules 的帮助下,LibraryB 可以导入 v3 和 v4,并对这两个版本进行类型断言。

package web

import (
 validationv3 "github.com/go-ozzo/ozzo-validation/v3"
 validationv4 "github.com/go-ozzo/ozzo-validation/v4"
)

// Error responds to a request with an error object and the specified status
func Error(w http.ResponseWriter, err error, status int) {
    // Implementation omitted for brevity
    errorsv3, ok := err.(validationv3.Errors)
    if ok {
        for key, err1 := range errorsv3 {
            // Implementation omitted for brevity
        }
        // Implementation omitted for brevity
    }
    // Implementation omitted for brevity


    errorsv4, ok := err.(validationv4.Errors)
    if ok {
        for key, err1 := range errorsv4 {
            // Implementation omitted for brevity
        }
        // Implementation omitted for brevity
    }
    // Implementation omitted for brevity

}