In Golang, we use structs with receiver methods. everything is perfect up to here.
I\'m not sure what interfaces are, however. We define methods in structs and if we wan
As has already been stated, interfaces are a tool. Not all packages will benefit from them, but for certain programming tasks interfaces can be an extremely useful for abstraction and creating package APIs, and particularly for library code or code which may be implemented in more than one way.
Take for example a package which is responsible for drawing some primitive graphics to a screen. We can think of the absolute basic essential requirements of a screen as being able to draw a pixel, clear the screen, refresh the screen contents periodically, as well as get some basic geometric information about the screen such its current dimensions. Hence a 'Screen' interface might look like this;
type Screen interface {
Dimensions() (w uint32, h uint32)
Origin() (x uint32, y uint32)
Clear()
Refresh()
Draw(color Color, point Point)
}
Now our program might have several different "graphics drivers" which could be used by our graphics package to fulfill this basic requirement of a Screen. You might be using some native operating system driver, maybe the SDL2 package and maybe something else. And maybe in your program you need to support multiple options for drawing graphics because its dependent upon the OS environment and so on.
So you might then define three structs, each containing the required resources to the underlying screen drawing routines in the operating system / libraries etc;
type SDLDriver struct {
window *sdl.Window
renderer *sdl.Renderer
}
type NativeDriver struct {
someDataField *Whatever
}
type AnotherDriver struct {
someDataField *Whatever
}
And then you implement in your code the method interface for all three of these structs so that any of these three structs can satisfy the requirements of the Screen interface
func (s SDLDriver) Dimensions() (w uint32, h uint32) {
// implement Dimensions()
}
func (s SDLDriver) Origin() (x uint32, y uint32) {
// implement Origin()
}
func (s SDLDriver) Clear() {
// implement Clear()
}
func (s SDLDriver) Refresh() {
// implement Refresh()
}
func (s SDLDriver) Draw(color Color, point Point) {
// implement Draw()
}
...
func (s NativeDriver) Dimensions() (w uint32, h uint32) {
// implement Dimensions()
}
func (s NativeDriver) Origin() (x uint32, y uint32) {
// implement Origin()
}
func (s NativeDriver) Clear() {
// implement Clear()
}
func (s NativeDriver) Refresh() {
// implement Refresh()
}
func (s NativeDriver) Draw(color Color, point Point) {
// implement Draw()
}
... and so on
Now, your outside program really shouldn't care WHICH of these drivers you might be using, just so long as it can clear, draw and refresh the screen through a standard interface. This is abstraction. You provide at the package level the absolute minimum that is required for the rest of your program to work. Only code inside graphics needs to know all of the "nitty gritty" of HOW the operations work.
So you might know which screen driver you need to create for the given environment, maybe this is decided at the start of execution based on checking what's available on the users system. You decide that SDL2 is the best option and you create a new SDLGraphics instance;
sdlGraphics, err := graphics.CreateSDLGraphics(0, 0, 800, 600)
But you can now create a type variable of Screen from this;
var screen graphics.Screen = sdlGraphics
And now you have a generic 'Screen' type called 'screen' which implements (assuming you programmed them) the Clear(), Draw(), Refresh(), Origin() and Dimensions() methods. From this point on in your code you can, in total confidence, issue statements such as
screen.Clear()
screen.Refresh()
And so on... The beauty of this is that you have a standard type called 'Screen' which the rest of your program, which really doesn't care about the inner workings of a graphics library, can use without having to think about it. You can pass around 'Screen' to any function etc in confidence that it will just work.
Interfaces are super useful and they really help you think about the function of your code rather than the data in your structs. And small interfaces are better!
For example instead of having a whole bunch of rendering operations inside the Screen interface, maybe you'll design a second interface like this;
type Renderer interface {
Fill(rect Rect, color Color)
DrawLine(x float64, y float64, color Color)
... and so on
}
It definitely takes some getting used to, depending on your programming experience and which languages you've used before. If you've been a strict python programmer up until now you'll find Go quite different, but if you've been using Java/C++ then you'll figure out Go pretty quickly. Interfaces give you object-oriented-ness without the annoyance that exists in other languages (e.g. Java).