Мені знайоме те, що в Go, інтерфейси визначають функціональність, а не дані. Ви вкладаєте набір методів в інтерфейс, але ви не можете вказати поля, які були б необхідні для будь-якого, що реалізує цей інтерфейс.
Наприклад:
// Interface
type Giver interface {
Give() int64
}
// One implementation
type FiveGiver struct {}
func (fg *FiveGiver) Give() int64 {
return 5
}
// Another implementation
type VarGiver struct {
number int64
}
func (vg *VarGiver) Give() int64 {
return vg.number
}
Тепер ми можемо використовувати інтерфейс та його реалізацію:
// A function that uses the interface
func GetSomething(aGiver Giver) {
fmt.Println("The Giver gives: ", aGiver.Give())
}
// Bring it all together
func main() {
fg := &FiveGiver{}
vg := &VarGiver{3}
GetSomething(fg)
GetSomething(vg)
}
/*
Resulting output:
5
3
*/
Тепер те, що ви не можете зробити, - це щось подібне:
type Person interface {
Name string
Age int64
}
type Bob struct implements Person { // Not Go syntax!
...
}
func PrintName(aPerson Person) {
fmt.Println("Person's name is: ", aPerson.Name)
}
func main() {
b := &Bob{"Bob", 23}
PrintName(b)
}
Однак, розібравшись з інтерфейсами та вбудованими структурами, я знайшов спосіб це зробити за модою:
type PersonProvider interface {
GetPerson() *Person
}
type Person struct {
Name string
Age int64
}
func (p *Person) GetPerson() *Person {
return p
}
type Bob struct {
FavoriteNumber int64
Person
}
Через вбудовану структуру у Боба є все, що у людини є. Він також реалізує інтерфейс PersonProvider, тому ми можемо передавати Бобу функції, призначені для використання цього інтерфейсу.
func DoBirthday(pp PersonProvider) {
pers := pp.GetPerson()
pers.Age += 1
}
func SayHi(pp PersonProvider) {
fmt.Printf("Hello, %v!\r", pp.GetPerson().Name)
}
func main() {
b := &Bob{
5,
Person{"Bob", 23},
}
DoBirthday(b)
SayHi(b)
fmt.Printf("You're %v years old now!", b.Age)
}
Ось майданчик Go, який демонструє наведений вище код.
Використовуючи цей метод, я можу створити інтерфейс, який визначає дані, а не поведінку, і який може бути реалізований будь-якою структурою лише шляхом вбудовування цих даних. Ви можете визначити функції, які явно взаємодіють із вбудованими даними та не знають про природу зовнішньої структури. І все перевіряється під час компіляції! (Тільки так ви могли б зіпсувати, що я можу бачити, буде вбудовування інтерфейсу PersonProvider
в Bob
, а не бетон Person
. Було б компілювати і не під час виконання.)
Тепер ось моє запитання: це акуратний трюк, чи я повинен робити це по-іншому?