返回嵌套结构的接口

时间:2019-03-24 10:37:26

标签: go interface

我正在尝试通过使用接口来打破大小合理的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的链接。

2 个答案:

答案 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)

对于您的情况,您可以实施三个程序包来解决循环进口:

  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
}

  1. 系统软件包

根据您的应用程序的配置生产系统。

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
}
  1. 第三方套餐

用法示例:

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