Полиморфизм с интерфейсами в 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. Вот пример:

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

  1. Мы создали объект для структуры типа Manager.

  2. Структура Manager содержит все функции, требующиеся для интерфейса Employee.

  3. Создана переменная типа Employee.

  4. Объект Manager присвоен переменной employeeInterface.

  5. employeeInterface теперь может использоваться для вызова функций, принадлежащих интерфейсу типа Employee.

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

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

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

  1. В Golang интерфейсы можно использовать как типы.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ссылки

https://nuancesprog.ru/p/6632/arrow-up-right

Last updated