Наследование в традиционных объектно-ориентированных языках предлагает три функции в одном. Когда Cat наследуется от Animal:
- класс Cat повторно использует код из класса Animal,
- переменная x типа Animal может ссылаться либо к Cat, либо к Animal,
- x.Eat() выберет метод Eat в зависимости от того, к какому типу относится объект x.
В объектно-ориентированном жаргоне эти функции известны как повторное использование кода (code reuse), полиморфизм и динамическая диспетчеризация (dynamic dispatch).
Все они доступны в Go, используя отдельные конструкции:
- композиция (composition) и встраивание (embedding) обеспечивают повторное использование кода,
- интерфейсы заботятся о полиморфизме и динамической диспетчеризации.
Повторное использование кода посредством композиции (composition)
Не беспокойтесь об иерархиях типов при запуске нового проекта Go - позже легко внедрить полиморфизм и динамическую диспетчеризацию.
Если Cat требуются некоторые или все функциональные возможности Animal, просто используйте композицию (composition).
type Animal struct {
// …
}
type Cat struct {
fluffy Animal
// …
}
Это дает вам полную свободу использования Animal части вашей Cat по мере необходимости.
Повторное использование кода посредством встраивания (embedding)
Если класс Cat наследует точное поведение Animal, такой подход может привести к утомительному написанию повторяющегося кода.
type Animal struct {
// …
}
func (a *Animal) Eat() { … }
func (a *Animal) Sleep() { … }
func (a *Animal) Walk() { … }
type Cat struct {
fluffy Animal
// …
}
func (a *Cat) Eat() { a.fluffy.Eat() }
func (a *Cat) Sleep() { a.fluffy.Sleep() }
func (a *Cat) Walk() { a.fluffy.Walk() }
Этот шаблон написания кода известен как делегирование.
Go использует встраивание для подобных ситуаций. Объявление структуры Cat и ее трех методов можно сократить до:
type Cat struct {
Animal
// …
}
Полиморфизм и динамическая диспетчеризация с интерфейсами
Держите свои интерфейсы короткими и вводите их только при необходимости.
В дальнейшем ваш проект мог бы включать больше животных. На этом этапе вы можете ввести полиморфизм и динамическую диспетчеризацию с использованием интерфейсов.
Если вам нужно усыпить всех своих питомцев, вы можете определить интерфейс Sleeper.
type Sleeper interface {
Sleep()
}
func main() {
pets := []Sleeper{new(Cat), new(Dog)}
for _, x := range pets {
x.Sleep()
}
}
Никакого явного объявления не требуется для типов Cat и Dog. Любой тип, который предоставляет методы, названные в интерфейсе, может рассматриваться как реализация.
Читайте также:
- Go FAQ: Является ли Go объектно-ориентированным языком?
- Go FAQ: Почему в Go нет наследования типов?
- Go FAQ: Почему в Go нет объявлений "implements"?
Комментариев нет:
Отправить комментарий