#go #interface
#Вперед #интерфейс
Вопрос:
Я все,
Я задаюсь вопросом о существенных различиях между интерфейсами и приемником указателей, потому что оба делают одно и то же.
Можете ли вы сказать мне, в чем разница между этими 2 фрагментами кода:
type Person struct {
Name string
Age int
}
func (p *Person) IncreaseAge(age int) *Person {
p.Age = age
return p
}
player1 := amp;Person{Age: 25,}
player1.IncreaseAge(55)
и этот с интерфейсом
type Increaser interface { IncreaseAge(age int) Person }
type Person struct {
Name string
Age int
}
func (p *Person) IncreaseAge(age int) *Person {
p.Age = age
return p
}
var player2 Increaser
player2 = amp;Person {Age: 25,}
player2.IncreaseAge(55)
Я не понимаю преимуществ интерфейсов в этом случае. Большое спасибо за вашу помощь
Комментарии:
1. «Разница» здесь на самом деле не означает. Вы говорите о 2 разных вещах. Мы можем
player2.IncreaseAge(55)
, потому что вы определилиIncreaseAge()
как метод дляPerson
, а во втором фрагменте вы просто сохраняете свойamp;Person {Age: 25,}
в переменную интерфейса, и вы можете назначить ей все, что реализовалоIncreaseAge()
метод.2. Здесь нет никакой пользы от наличия интерфейса, поскольку также нет причин использовать интерфейс. Если нет никакой причины, не используйте ее.
3. Я знаю, что нет никаких преимуществ в интерфейсе heure. Дело в том, что я не очень хорошо разбираюсь в интерфейсах
Ответ №1:
Интерфейсы имеют смысл только тогда, когда существует несколько разных типов, которые имеют некоторое общее поведение (четко определенное как набор сигнатур функций), но различную реализацию этого поведения, и вы хотите передать эти разные типы в качестве аргумента в единую функцию, которая может на основе этого общего поведения, определенного интерфейсом (и, возможно, некоторымивнешние входы) делают что-то независимо от того, какой конкретный тип был передан ему (при условии, что этот тип реализует данный интерфейс).
Допустим, вы хотите добавить тип Dog
и метод Introduce()
к обоим Person
, и Dog
это вернет вводное сообщение. В случае Person
метода Introduce()
должно быть возвращено введение с именем и возрастом этого конкретного человека, в случае Dog
так bark bark
же, как собаки не могут говорить на человеческом языке. Теперь где-то в коде вы хотите иметь единственную функцию SayHi(x)
, которая не будет иметь значения, является ли x
Person
она или Dog
выводится Introduce()
на печать (у них обоих такое «поведение»). Это был бы допустимый вариант использования интерфейса:
package main
import "fmt"
type Person struct {
Name string
Age int
}
type Dog struct {
Name string
Age int
}
func (p Person) Introduce() string {
return fmt.Sprintf("I'm a person named %s and %d years old", p.Name, p.Age)
}
func (d Dog) Introduce() string {
// Dogs cannot talk to human so they just bark to introduce themselves
return fmt.Sprintf("bark bark!")
}
type Introducer interface {
Introduce() string
}
func SayHi(i Introducer) {
fmt.Println(i.Introduce())
}
func main() {
p := Person{"Timmy", 7}
d := Dog{"Lassie", 5}
// Here you can use SayHi no matter if it is Person or Dog...
SayHi(p)
SayHi(d)
}
Игровая площадка: https://play.golang.org/p/wjHgPu7Esvr
Один из хороших примеров интерфейса — io.Reader
из стандартной библиотеки. Который описывает поведение возможности считывания байтов (наличие метода Read(p []byte) (int, error)
). Существует множество различных реализаций этого интерфейса, которые могут считывать данные из файла, сетевого подключения, архива, шифрования и т. Д. Благодаря существованию io.Reader
вы можете написать функцию, которой все равно, откуда поступают байты.
Если вам не нужно этого делать, очевидно, вам не нужно создавать интерфейс. Чрезмерное использование интерфейсов там, где они не нужны, приводит к загромождению кода. В идеале начните без них, и если вы окажетесь в положении, когда обнаружите общность или нуждаетесь в общем поведении в нескольких типах, реорганизуйте код и создайте интерфейс.
Комментарии:
1. Я не понимаю ваш пример, потому что и person, и dog объявлены как struct, а не interface .. и ваша функция SayHi принимает интерфейс вводного элемента в качестве аргумента ?!
2. @user1310969 В этом весь смысл интерфейса. Вместо конкретного типа вы используете интерфейс (в exchange вы можете использовать только методы, указанные этим интерфейсом), а затем может быть передан любой тип, который реализует эти методы (реализует интерфейс). Итак, мой интерфейс
Introducer
— это и то, и другоеPerson
, иDog
реализовать этот интерфейс, поэтому мне нужен только одинSayHi()
.