【问题标题】:Go interface return typeGo接口返回类型
【发布时间】:2019-05-10 09:11:31
【问题描述】:

我有一个这样的界面:

type ViewInterface interface{
    Init() View
}

type View struct{
    Width  int
    Height int
}

所以我从 View 创建了一个新类型

type MainView View

func (m MainView) Init() MainView{
 return MainView{
   Width:10,
   Height:10,
 }
}

然后我将 MainView 传递给以下方法:

func Render(views ...ViewInterface){
  for _, view := range views {
     v := view.Init()
  }
}

func main() {
  Render(MainView{})
}

但我收到此错误:

不能使用 MainView 文字(MainView 类型)作为 ViewInterface 类型 Render 的参数:MainView 没有实现 ViewInterface(错误 初始化方法的类型)
有 Init() MainView
想要 Init() 查看

为什么MianViewView 不一样?解决这个问题的正确方法是什么?

谢谢

【问题讨论】:

    标签: go go-interface


    【解决方案1】:

    与 Java 和 C# 等主流语言相比,Go 具有不同的继承模型。

    为什么 MianView 和 View 不一样?

    因为它们的定义不同。

    MainViewInit函数返回MainView,而接口要求返回View

    Init 方法的签名看起来很奇怪,它需要结构体实例,因为它是结构体方法并返回相同结构体类型的新实例。

    尝试围绕结构的逻辑而不是它们的构造/生命周期来设计界面:

    type ViewInterface interface{
        Render()
    }
    
    type MainView View
    
    func (m MainView) Render() {
      // do something
    }
    
    type AnotherView
    
    func (m AnotherView) Render() {
      // do something else
    }
    
    func Render(views ...ViewInterface){
      for _, view := range views {
         view.Render()
      }
    }
    

    【讨论】:

    • 更准确地说“Go没有没有继承模型”
    【解决方案2】:

    因为type MainView View"defined type""is different from any other type, including the type it is created from."

    您可以改为使用type aliastype MainView = View.


    但真正的问题在于ViewInterfaceInit() 的设计。

    Init() 写成类方法。 Go 没有类方法(或者,严格来说,类)。您创建结构并在其上调用方法。然后就可以进行简单的初始化了。

    view := View{ Width: 10, Height: 10 }
    

    如果您想定义一个方法来一致地初始化值,它将作用于现有结构并且不返回任何内容。

    type ViewInterface interface{
        Init()
    }
    
    type View struct{
        Width  int
        Height int
    }
    
    func (v *View) Init() {
        v.Width = 10
        v.Height = 10
    }
    
    view := View{}
    view.Init()
    

    那么MainView也可以定义Init()

    type MainView struct {
        X int
        Y int
    }
    
    type (mv *MainView) Init() {
        mv.X = 23
        mv.Y = 42
    }
    

    因为Init() 需要一个指针,所以要满足ViewInterface 你必须传入指针。

    func main() {
        view := View{}
        mv := MainView{}
        Render(&view, &mv)
    }
    

    但是Render() 到底在做什么初始化对象呢?那应该已经完成​​了。它应该是渲染。接口应该是关于通用功能的,而不考虑它是如何实现的。实现 ViewInterface 的东西应该已经被初始化了。

    相反,您可能会说ViewInterface 必须具有Render 方法。

    type ViewInterface interface{
        Render()
    }
    

    然后ViewMainView 可以任意构造,只要它们实现Render()

    func (v View) Render() {
        fmt.Println("View!")
        fmt.Println(v)
    }
    
    func (mv MainView) Render() {
        fmt.Println("MainView!")
        fmt.Println(mv)
    }
    

    然后aRender() 可以获取实现ViewInterface 的事物列表并在每个事物上调用Render()

    func Render(views ...ViewInterface){
      for _, view := range views {
         view.Render()
      }
    }
    

    在传入之前初始化它们。现在不需要传递指针了。

    func main() {
        view := View{}
        view.Init()
        mv := MainView{}
        mv.Init()
        Render(view, mv)
    }
    

    最后,Markus suggested in the comments 使用包来获取类方法之类的东西。

    # viewtest/main.go
    package main
    
    import(
        "log"
        "viewtest/view"
    )
    
    func main() {
        v := view.New()
        log.Printf("%#v", v)
    }
    
    
    # viewtest/view/view.go
    package view
    
    type View struct {
        Width  int
        Height int
    }
    
    func New() View {
        return View{Width: 10, Height: 10}
    }
    

    Go 包需要一点时间来适应,Go 对如何构建项目有坚定的想法。 I suggest this tutorial.

    【讨论】:

    • 总是很高兴看到人们在回答问题时加倍努力!我想补充一点,view.New(<args>) View 不仅看起来更惯用,而且在这种情况下也更直观。
    • @MarkusWMahlberg 谢谢。 view.New(<args>) View 我不知道那会怎样。
    • @MarkusWMahlberg OIC。介意我加进去吗?
    • 一点也没有。 (很难用 15 个字符或更多字符说“是”;))
    猜你喜欢
    • 2016-03-15
    • 1970-01-01
    • 1970-01-01
    • 2017-01-25
    • 2013-06-11
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    • 2011-02-10
    相关资源
    最近更新 更多