我有这样的界面:
type ViewInterface interface{
Init() View
}
type View struct{
Width int
Height int
}
Run Code Online (Sandbox Code Playgroud)
所以我从View创建了一个新类型
type MainView View
func (m MainView) Init() MainView{
return MainView{
Width:10,
Height:10,
}
}
Run Code Online (Sandbox Code Playgroud)
然后我将MainView传递给以下方法:
func Render(views ...ViewInterface){
for _, view := range views {
v := view.Init()
}
}
func main() {
Render(MainView{})
}
Run Code Online (Sandbox Code Playgroud)
但我得到这个错误:
不能使用MainView文字(类型MainView)作为Render参数中的类型ViewInterface:MainView没有实现ViewInterface(Init方法的错误类型)
有Init()MainView
想要Init()视图
为什么MianView不一样View?解决这个问题的正确方法是什么?
谢谢
与Java和C#等主流语言相比,GO具有不同的继承模型.
为什么MianView与View不一样?
因为它们的定义不同.
Init函数MainView返回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()
}
}
Run Code Online (Sandbox Code Playgroud)
因为type MainView View是“定义的类型”并且“不同于任何其他类型,包括创建它的类型。 ”。
相反,您可以使用类型别名。type MainView = View.
但真正的问题在于ViewInterfaceand的设计Init()。
Init()写得像一个类方法。Go 没有类方法(或者,严格来说,没有类)。您创建结构并在其上调用方法。然后就可以进行简单的初始化。
view := View{ Width: 10, Height: 10 }
Run Code Online (Sandbox Code Playgroud)
如果您想定义一个方法来一致地初始化值,它将作用于现有结构并且不返回任何内容。
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()
Run Code Online (Sandbox Code Playgroud)
然后MainView也可以定义Init().
type MainView struct {
X int
Y int
}
type (mv *MainView) Init() {
mv.X = 23
mv.Y = 42
}
Run Code Online (Sandbox Code Playgroud)
因为Init()需要一个指针,为了满足ViewInterface你必须传入指针。
func main() {
view := View{}
mv := MainView{}
Render(&view, &mv)
}
Run Code Online (Sandbox Code Playgroud)
但是Render()初始化对象到底在做什么呢?那应该已经完成了。应该是渲染。接口应该是关于通用功能的,而不管它是如何实现的。实现 ViewInterface 的东西应该已经初始化了。
相反,您可能会说 aViewInterface必须有一个Render方法。
type ViewInterface interface{
Render()
}
Run Code Online (Sandbox Code Playgroud)
然后View和MainView可以按照您喜欢的方式进行结构化,只要它们实现Render().
func (v View) Render() {
fmt.Println("View!")
fmt.Println(v)
}
func (mv MainView) Render() {
fmt.Println("MainView!")
fmt.Println(mv)
}
Run Code Online (Sandbox Code Playgroud)
然后 aRender()可以获取实现ViewInterface和调用Render()它们中的每一个的事物的列表。
func Render(views ...ViewInterface){
for _, view := range views {
view.Render()
}
}
Run Code Online (Sandbox Code Playgroud)
在传入它们之前初始化它们。现在不需要传递指针了。
func main() {
view := View{}
view.Init()
mv := MainView{}
mv.Init()
Render(view, mv)
}
Run Code Online (Sandbox Code Playgroud)
最后,Markus 在评论中建议使用包来获取类方法之类的东西。
# 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}
}
Run Code Online (Sandbox Code Playgroud)
Go 包需要一点时间来适应,Go 对您的项目必须如何构建有明确的想法。我推荐这个教程。