# Полиморфизм с интерфейсами в Golang

**Интерфейсы в Golang** работают в совершенно особенной манере в сравнении с интерфейсами на других языках серверного программирования. Прежде чем углубляться в тему, начнём с базовых понятий. **Интерфейсы в Golang предоставляют список сигнатур функций**, которые **реализовываются** любой «структурой» **для работы с конкретными интерфейсами**. Давайте разберёмся, что под этим подразумевается.

Особенности интерфейсов:

1. Интерфейсы определяют контракт функции.
2. Интерфейсы в Golang представлены типами.

Давайте определим простой интерфейс для сотрудника `employee`…

```
type Employee interface {
  GetDetails() string
  GetEmployeeSalary() int
}
```

Здесь к интерфейсу `Employee` мы добавляем две функции — `GetDetails` и `GetEmployeeSalary`. Любая структура или тип, которым нужно работать с интерфейсом `Employee`, должна содержать эти функции, указанные как контракт интерфейса. Так используются преимущества интерфейса.

Теперь определим новый тип `Manager`, реализующий эти функции контракта, чтобы и он мог воспользоваться преимуществами интерфейса `Employee`.

```
type Manager struct {
  Name string
  Age int
  Designation string 
  Salary int
}

func (mgr Manager) GetDetails() string {
  return mgr.Name + " " + mgr.Age;
}

func (mgr Manager) GetEmployeeSalary int {
  return mgr.Salary
}
```

Здесь у нас определён `тип`, который выполняет контракт, указанный интерфейсом `Employee`. Давайте посмотрим, какие преимущества предлагает интерфейс при работе с этими вновь определёнными типами.

#### Интерфейсы в Golang представлены «типами»

Интерфейсы **можно использовать как «типы» в Golang**, то есть мы можем создать переменные типа `Interface` и любая структура, выполняющая контракт на функцию, может быть присвоена этой переменной `Interface`.

Так же как `Manager`, объявляя любые типы, содержащие все функциональные требования к контракту интерфейса `Employee`, мы сможем создать их объекты и присвоить их переменной `Interface`. Вот пример:

```
newManager := Manager{Name: "Mayank", Age: 30, Designation: "Developer" Salary: 10}

var employeeInterface Employee

employeeInterface = newManager

employeeInterface.GetDetails()
```

С**тоит сделать несколько замечаний:**

1. Мы создали объект для **структуры** типа `Manager`.
2. Структура `Manager` содержит все функции, требующиеся для интерфейса `Employee`.
3. Создана переменная типа `Employee`.
4. Объект `Manager` присвоен переменной `employeeInterface`.
5. `employeeInterface` теперь может использоваться для вызова функций, принадлежащих интерфейсу типа `Employee`.

Здесь мы создали переменную интерфейсного типа, и любая структура, содержащая все функции, указанные в контракте интерфейса, может быть ей присвоена. Следовательно, мы могли присвоить объект типа `Manager` интерфейсу типа `Employee`.

#### Интерфейсы в Golang отличаются своеобразием…

**Эта особенность заметно выделяет** его по реализации интерфейса на фоне других серверных языков. Главные выводы, которые можно сделать на основе этого кода:

1. В Golang интерфейсы можно использовать как типы.
2. Объект, имеющий все интерфейсные функции, можно присвоить переменной интерфейса.

**Давайте напишем весь сценарий**

```
// Создание простого интерфейса Employee
type Employee interface {
  GetDetails() string
  GetEmployeeSalary() int
}

// Создание нового типа Manager, который содержит все функции, требующиеся интерфейсу Employee
type Manager struct {
  Name string
  Age int
  Designation string 
  Salary int
}

func (mgr Manager) GetDetails() string {
  return mgr.Name + " " + mgr.Age;
}

func (mgr Manager) GetEmployeeSalary int {
  return mgr.Salary
}

// Создание нового объекта типа Manager
newManager := Manager{Name: "Mayank", Age: 30, Designation: "Developer" Salary: 10}

// Создана новая переменная типа Employee
var employeeInterface Employee

// Объект Manager присвоен интерфейсному типу, потому что контракт интерфейса выполнен
employeeInterface = newManager

// Вызов функций, принадлежащих интерфейсу Employee
employeeInterface.GetDetails()
```

#### Полиморфизм с интерфейсами Golang

То, что мы можем создавать переменную типа `Interface` и присваивать ей объект `Struct`, даёт дополнительное преимущество. Теперь появилась возможность присваивать объекты разных типов интерфейсу, который выполняет функциональный контракт и вызывает из него функцию. То есть мы имеем дело с **полиморфизмом**.

**Давайте сделаем реализацию другого типа `Struct`** со всеми функциями, требующимися интерфейсу `Employee`. **Создадим новый тип `Lead`**, реализующий эти функции. Он содержит все функции, указанные в контракте интерфейса. Значит, можно присвоить объект переменным интерфейса.

```
type Employee interface {
  GetDetails() string
  GetEmployeeSalary() int
}

// Создание нового типа Manager, который содержит все функции, требующиеся интерфейсу Employee
type Manager struct {
  Name string
  Age int
  Designation string 
  Salary int
}

func (mgr Manager) GetDetails() string {
  return mgr.Name + " " + mgr.Age;
}

func (mgr Manager) GetEmployeeSalary int {
  return mgr.Salary
}

// Создание нового типа Lead, который содержит все функции, требующиеся интерфейсу Employee
type Lead struct {
  Name string
  Age int
  TeamSize string 
  Salary int
}

func (ld Lead) GetDetails() string {
  return ld.Name + " " + ld.Age;
}

func (ld Lead) GetEmployeeSalary int {
  return ld.Salary
}

// Создание нового объекта типа Manager
newLead := Lead{Name: "Mayank", Age: 30, TeamSize: "30" Salary: 10}

// Создана новая переменная типа Employee
var employeeInterface Employee

// Объект Manager присвоен интерфейсному типу, потому что контракт интерфейса выполнен
employeeInterface = newManager

// Вызов функций, принадлежащих интерфейсу Employee
employeeInterface.GetDetails()

// Тот же интерфейс может хранить значение объекта Lead
employeeInterface = newLead

// Вызов функций объекта Lead, присвоенного интерфейсу Employee
employeeInterface.GetDetails()
```

В этом коде мы присваиваем переменной интерфейса либо объект типа `Lead`, либо объект типа `Manager`. Получаем, таким образом, общий интерфейс для вызова одной и той же функции, принадлежащей разным типам. **Вот он наш полиморфизм.**

#### Использование интерфейса в качестве параметра функции

Интерфейс может быть добавлен в функцию в качестве параметра. В этом случае параметр входного значения может принимать любой объект, удовлетворяющий контракту интерфейса, и затем использоваться для вызова из него функций. Это форма полиморфизма времени выполнения, где одну и ту же переменную можно использовать для вызова функции из разных типов объекта. Вот пример:

```
// Функция, принимающая интерфейс в качестве входного параметра
func GetUserDetails(emp Employee) {
  emp.GetDetails()
}

type Employee interface {
  GetDetails() string
}

// Создание нового типа Manager, который содержит все функции, требующиеся интерфейсу Employee
type Manager struct {
  Name string
  Age int
  Designation string 
  Salary int
}

func (mgr Manager) GetDetails() string {
  return mgr.Name + " " + mgr.Age;
}

// Создание нового типа Lead, который содержит все функции, требующиеся интерфейсу Employee
type Lead struct {
  Name string
  Age int
  TeamSize string 
  Salary int
}

func (ld Lead) GetDetails() string {
  return ld.Name + " " + ld.Age;
}

// Создание нового объекта типа Manager
newLead := Lead{Name: "Mayank", Age: 30, TeamSize: "30" Salary: 10}

// Создана новая переменная типа Employee
var employeeInterface Employee

// Объект Manager присвоен интерфейсному типу, потому что контракт интерфейса выполнен
GetUserDetails(newManager)

// Интерфейс можно использовать для вызова функции Lead или Manager...
GetUserDetails(newLead)
```

Оба типа объектов реализуют функцию, принадлежащую интерфейсу, поэтому они могут передаваться любой функции, принимающей интерфейс в качестве входного параметра.

#### Использование интерфейсов как типов в структурах

Определяя новый тип данных, мы можем использовать интерфейс в качестве типа параметра (если тип параметра определяется как интерфейс). Любую структуру, реализующую контракт функции, можно присвоить этому параметру.

Покажем это на простых примерах:

```
type Person struct {
  name string
  age string 
  user Employee
}
```

Здесь структура `Person` содержит параметр `User` интерфейсного типа `Employee`. Попробуем присвоить параметру `User` различные структуры `Lead` и `Manager`.

```
type Person struct {
  name string
  age string 
  user Employee
}

// Присвоение объекта типа Manager параметру User
newPerson := Person{name: "Mayank", age: "32", Manager{Name: "Mayank", Age: 30, Designation: "Developer" Salary: 10}}

// Присвоение объекта типа Lead параметру User
newPerson := Person{name: "Mayank", age: "32", Lead{Name: "Mayank", Age: 30, TeamSize: "30" Salary: 10}}
```

Здесь интерфейс используется в качестве типа параметра `Struct`.

#### Ссылки

<https://nuancesprog.ru/p/6632/>
