我正在尝试通过使用接口来打破大小合理的golang项目中的循环依赖关系。我有一些这样的嵌套结构:
// these are in one package...
type config struct {
region string
}
type system struct {
name string
config config
}
func (s system) getName() string {
return s.name
}
func (s system) getConfig() config {
return s.config
}
func (c config) getRegion() string {
return c.region
}
在另一个要使用它们的包中,我声明了一些对应的嵌套接口:
type iConfig interface {
getRegion() string
}
type iSystem interface {
getName() string
getConfig() iConfig
}
// and has functions like
func New(system iSystem) {
fmt.Printf("region=%s", system.getConfig().getRegion())
}
但是当我尝试像这样使用它们时:
theSystem := system{
name: "testName",
config:config{
region:"testRegion",
},
}
New(theSystem) // doesn't work
我得到一个错误:
cannot use theSystem (type system) as type iSystem in argument to New:
system does not implement iSystem (wrong type for getConfig method)
have getConfig() config
want getConfig() iConfig
似乎因为我的具体system
结构返回了具体类型config
Go认为它不能满足iConfig
接口-即使我可以通过以下方式使用config
iConfig
接口。我期望config
隐式地满足iConfig
接口,但这没有发生。我该如何解决?
这里是Go Playground的链接。
答案 0 :(得分:1)
接口是其他类型可以遵循的签名/合同。
签名可以包含一个或多个方法签名,这意味着方法名称,参数(包括类型)和返回参数。如果它不包含任何方法,则实际上是臭名昭著的interface{}
类型,每种类型都遵循该类型。
要遵守接口,类型必须严格实现完整的签名,包括所有传递和返回的参数及其类型。
接口和结构是不同的类型。
因此,在下面的示例中,Type2
没有实现Intf2
:
type Intf1 interface{
Bar()
}
type Type1 struct {}
func (s SomeType) Bar() {}
type Intf2 interface{
Foo() Intf1
}
// Type2 does not implement Intf2
type Type2 struct {}
func (s Type2) Foo() Type1 {}
// Won't compile
var _ Intf2 = Type2{}
因为go编译器认为Foo
上方法Intf2
的签名不同,因为返回类型不同。编译器不会推断返回类型也实现了该接口,因为这样做会带来很多复杂性。
如果您希望本示例能够正常工作,则需要将Type2
更改为此:
// Type2 does not implement Intf2
type Type2 struct {}
func (s Type2) Foo() Intf1 {}
这也适用于传递的参数,不仅是返回参数。
现在,关于循环依赖关系,我建议您将接口暴露在第三个包中,该包充当粘合和顶层包。要做的常见事情之一是拥有一个包含接口的主程序包,以实现其主要目的。
示例:
pkg config
type Source interface{}
type Parser interface{}
pkg parsers
pkg jsonparser
implements config.Parser
pkg yamlparser
implements config.Parser
pkg sources
pkg filesource
implements config.Source
```
答案 1 :(得分:1)
对于您的情况,您可以实施三个程序包来解决循环进口:
获取该应用程序的配置,并以方便的方式为其组件提供它。
package config
type IConfig interface {
GetRegion() string
}
type config struct {
region string
}
func New(region string) IConfig {
return &config{
region: region,
}
}
func (c config) GetRegion() string {
return c.region
}
根据您的应用程序的配置生产系统。
package system
import (
// "config"
)
type ISystem interface {
GetName() string
GetConfig() config.IConfig
}
type system struct {
name string
config config.IConfig
}
func New(name string, cfg config.IConfig) ISystem {
return &system{
name: name,
config: cfg,
}
}
func (s system) GetName() string {
return s.name
}
func (s system) GetConfig() config.IConfig {
return s.config
}
用法示例:
package main
import (
"fmt"
// "config"
// "system"
)
func UseConfig(cfg config.IConfig) {
fmt.Printf("region=%s\n", cfg.GetRegion())
}
func UseSystem(s system.ISystem) {
fmt.Printf("region=%s\n", s.GetConfig().GetRegion())
}
func main() {
cfg := config.New("myregion")
s := system.New("mysystem", cfg)
UseConfig(cfg)
UseSystem(s)
}