Go interface return type
Go has different inheritance model comparing to mainstream languages like Java and C#.
Why MianView is not same as View?
Because they are defined differently.
Init
function of MainView
returns MainView
while interface requires to return View
.
Signature of Init
method looks strange, it requires instance of structure because it is structure method and returns new instance of the same structure type.
Try to design interface around logic of your structures instead of their construction/lifetime:
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()
}
}
Because type MainView View
is a "defined type" and "is different from any other type, including the type it is created from.".
Instead you can use a type alias. type MainView = View
.
But really the problem is the design of ViewInterface
and Init()
.
Init()
is written like a class method. Go doesn't have class methods (or, strictly speaking, classes). You create the struct and call methods on it. Simple initialization can be done right then.
view := View{ Width: 10, Height: 10 }
If you want to define a method to initialize the values consistently it would act on an existing struct and return nothing.
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()
Then MainView
can also define Init()
.
type MainView struct {
X int
Y int
}
type (mv *MainView) Init() {
mv.X = 23
mv.Y = 42
}
Because Init()
takes a pointer, to satisfy ViewInterface
you have to pass in pointers.
func main() {
view := View{}
mv := MainView{}
Render(&view, &mv)
}
But what is Render()
doing initializing objects anyway? That should already be done. It should be rendering. Interfaces should be all about common functionality with no regard to how its implemented. The things implementing ViewInterface should already be initialized.
Instead, you might say that a ViewInterface
must have a Render
method.
type ViewInterface interface{
Render()
}
Then View
and MainView
can be structured however you like as long as they implement Render()
.
func (v View) Render() {
fmt.Println("View!")
fmt.Println(v)
}
func (mv MainView) Render() {
fmt.Println("MainView!")
fmt.Println(mv)
}
Then aRender()
can take a list of things which implement ViewInterface
and call Render()
on each of them.
func Render(views ...ViewInterface){
for _, view := range views {
view.Render()
}
}
Initialize them before passing them in. And now there's no need to pass pointers.
func main() {
view := View{}
view.Init()
mv := MainView{}
mv.Init()
Render(view, mv)
}
Finally, Markus suggested in the comments using a package to get something like class methods.
# 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 packages take a little getting used to, Go has firm ideas about how your project must be structured. I suggest this tutorial.