Полиморфизм с интерфейсами в Golang
Интерфейсы в Golang работают в совершенно особенной манере в сравнении с интерфейсами на других языках серверного программирования. Прежде чем углубляться в тему, начнём с базовых понятий. Интерфейсы в Golang предоставляют список сигнатур функций, которые реализовываются любой «структурой» для работы с конкретными интерфейсами. Давайте разберёмся, что под этим подразумевается.
Особенности интерфейсов:
Интерфейсы определяют контракт функции.
Интерфейсы в 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()Стоит сделать несколько замечаний:
Мы создали объект для структуры типа
Manager.Структура
Managerсодержит все функции, требующиеся для интерфейсаEmployee.Создана переменная типа
Employee.Объект
Managerприсвоен переменнойemployeeInterface.employeeInterfaceтеперь может использоваться для вызова функций, принадлежащих интерфейсу типаEmployee.
Здесь мы создали переменную интерфейсного типа, и любая структура, содержащая все функции, указанные в контракте интерфейса, может быть ей присвоена. Следовательно, мы могли присвоить объект типа Manager интерфейсу типа Employee.
Интерфейсы в Golang отличаются своеобразием…
Эта особенность заметно выделяет его по реализации интерфейса на фоне других серверных языков. Главные выводы, которые можно сделать на основе этого кода:
В Golang интерфейсы можно использовать как типы.
Объект, имеющий все интерфейсные функции, можно присвоить переменной интерфейса.
Давайте напишем весь сценарий
// Создание простого интерфейса 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.
Ссылки
Last updated
Was this helpful?