Technical note: first method assures you (besides mistakes in initializing the struct) you can call methodA
on Bar
, second one don't because you have to initialize the interface field with something respecting that interface to not have a nil deference error.
Note that using the second method methodA
is not called on Bar
but on the Foo
embedded object!
Second method is useful if you have a common implementation that can be shared by many objects and is self containing, i.e. consider you wanna know if an object implements a Log
method to be sure you can log something with that object: in this case you can have a method that returns a Logger
and set the interface field with that. Example follows:
package main
import "fmt"
type Logger interface {
Log(string)
}
type ConsoleLogger struct {} // See? No external dependencies
func (Cl ConsoleLogger) Log(msg string) {
fmt.Println(msg)
}
type A1 struct {
Logger
}
type A2 struct {
Logger
}
func main() {
a := A1{ConsoleLogger{}}
b := A2{ConsoleLogger{}}
a.Log("Test")
b.Log("Test")
}
Embedding objects is useful for dispatching method calls, remember it's just sintactic sugar in the end so besides passing the containing object you don't have any way to use its fields.
If Logger
interface had to use outer object (A1
and A2
) data in some sort of way than this method would be awkward because you would have to initialize the interface object which would then store some reference to the needed data with a waste of memory in some cases.
IMHO method one forces you to write more code but you are more free in the interface implementation and you can mix the two approaches by embedding a Logger
and then override the Log
method in A1
struct.
Additionally, you can nonetheless pass something to build something implementing an interface:
package main
import "fmt"
type Logger interface {
Log(string)
}
type ConsoleLogger struct {
Prepend string // string to prepend to log message
}
func (Cl ConsoleLogger) Log(msg string) {
fmt.Println(Cl.Prepend + "-" + msg)
}
type A1 struct {
Logger
}
type A2 struct {
Logger
}
func (a A2) Log(msg string) { // Overriding implementation
fmt.Println("In A2")
a.Logger.Log(msg) // Call the original interface value!
}
func main() {
a := A1{ConsoleLogger{"A1"}}
b := A2{ConsoleLogger{"A2"}}
a.Log("Test")
b.Log("Test")
}