Вопрос об интерфейсах и приемнике указателей в Go

#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() .