Menu
Coddy logo textTech

Полиморфизм через интерфейсы

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 41 из 107.

Polymorphism позволяет единообразно обращаться с разными типами через общий interface. В Go это достигается исключительно с помощью interface, без наследования или иерархий классов.

Когда function accepts тип interface в качестве параметра, в него можно передать любой конкретный тип, который реализует этот interface. function не нужно знать конкретный тип. Её интересует только поведение, определённое interface:

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

Теперь MakeSound работает с любым типом, у которого есть метод Speak():

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Гав!
    MakeSound(cat)  // Мяу!
}
MakeSound написана один раз, но работает с неограниченным количеством типов, если они удовлетворяют интерфейсу Speaker.

Этот подход делает ваш код гибким и расширяемым. Добавление нового типа, который издаёт звуки, не требует изменений в существующих функциях: просто реализуйте interface, и всё заработает автоматически.

challenge icon

Задание

Легко

Давайте создадим систему описания транспортных средств, демонстрирующую полиморфизм в действии. Вы создадите разные типы транспортных средств, которые используют общее поведение через интерфейс, а затем напишете одну функцию, работающую с любым транспортным средством.

Вы организуете код в двух файлах:

  • vehicles.go: Define интерфейс Describer, требующий наличия method Describe() string. Затем создайте три типа транспортных средств, каждый из которых по-своему Implement этот интерфейс:
    • Car с полями Brand и Model. Его Describe() возвращает Car: [Brand] [Model]
    • Motorcycle с полями Brand и EngineCC (int). Его Describe() возвращает Motorcycle: [Brand] [EngineCC]cc
    • Bicycle с полем Type (например, Mountain или Road). Его Describe() возвращает Bicycle: [Type]
  • main.go: Создайте function с именем PrintDescription, которая accepts любой Describer и выводит результат вызова Describe(). Считайте details транспортных средств из input, создайте по одному экземпляру каждого типа транспортного средства и передайте каждый из них в PrintDescription, чтобы продемонстрировать, что одна и та же function работает со всеми тремя разными типами.

Будут предоставлены следующие входные данные:

  • Строка 1: марка Car
  • Строка 2: Model Car
  • Строка 3: марка Motorcycle
  • Строка 4: объём двигателя Motorcycle в CC (целое число)
  • Строка 5: тип Bicycle

Например, если заданы Toyota, Camry, Honda, 600 и Mountain, результат должен быть следующим:

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

Обратите внимание: PrintDescription не нужно знать, получает ли она Car, Motorcycle или Bicycle. Она просто вызывает Describe(), и каждый тип отвечает своим уникальным результатом. Это и есть полиморфизм: одна function, несколько вариантов поведения.

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// TODO: Создайте функцию под названием PrintDescription, которая принимает любой Describer
// и выводит результат вызова Describe()

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Читаем данные автомобиля
	carBrand, _ := reader.ReadString('\n')
	carBrand = strings.TrimSpace(carBrand)
	carModel, _ := reader.ReadString('\n')
	carModel = strings.TrimSpace(carModel)

	// Читаем данные мотоцикла
	motoBrand, _ := reader.ReadString('\n')
	motoBrand = strings.TrimSpace(motoBrand)
	motoEngineStr, _ := reader.ReadString('\n')
	motoEngineStr = strings.TrimSpace(motoEngineStr)
	motoEngine, _ := strconv.Atoi(motoEngineStr)

	// Читаем данные велосипеда
	bicycleType, _ := reader.ReadString('\n')
	bicycleType = strings.TrimSpace(bicycleType)

	// TODO: Создайте Car, Motorcycle и Bicycle, используя введённые значения

	// TODO: Вызовите PrintDescription для каждого транспортного средства, чтобы продемонстрировать полиморфизм
	fmt.Println("TODO: Print vehicle descriptions")
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Go