Техническое примечание: первый метод гарантирует вам (помимо ошибок при инициализации структуры), что вы можете вызвать methodA
для Bar
, второй - нет, потому что вы должны инициализировать поле интерфейса чем-то, что не имеет этого интерфейса, чтобы не иметь ошибка нуля почтения.
Обратите внимание, что использование второго метода methodA
вызывается не для Bar
, а для встроенного объекта Foo
!
Второй метод полезен, если у вас есть общая реализация, которая может совместно использоваться многими объектами и является самодостаточной, т. Е. Вы хотите знать, реализует ли объект метод Log
, чтобы быть уверенным, что вы можете что-то регистрировать с этим объектом: в этом случае вы можете иметь метод, который возвращает Logger
и установить поле интерфейса с этим. Пример следует:
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")
}
Встраивание объектов полезно для диспетчеризации вызовов методов, помните, что в конце концов это просто синтаксический сахар, поэтому, кроме передачи содержащего объекта, вы не можете использовать его поля.
Если бы Logger
интерфейс должен был использовать данные внешнего объекта (A1
и A2
) каким-то образом, этот метод был бы неудобен, потому что вам пришлось бы инициализировать объект интерфейса, который затем сохранял бы некоторую ссылку на необходимые данные с тратой памяти в некоторых случаях.
ИМХО первый метод заставляет вас писать больше кода, но вы более свободны в реализации интерфейса, и вы можете смешать два подхода, внедрив Logger
, а затем переопределить метод Log
в A1
struct.
Кроме того, вы можете , тем не менее, передать что-то для создания чего-либо, реализующего интерфейс:
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")
}