Menu
Coddy logo textTech

Полиморфные коллекции

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

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

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

type Shape interface {
    Area() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
        Circle{Radius: 2},
    }
    
    for _, s := range shapes {
        fmt.Printf("Area: %.2f\n", s.Area())
    }
}

Срез shapes содержит и круги, и прямоугольники. При переборе каждый элемент реагирует на Area() в соответствии со своим фактическим типом. Это полиморфизм, применённый к коллекциям: один и тот же цикл единообразно обрабатывает все типы фигур.

Полиморфные коллекции необходимы при создании систем, которые обрабатывают различные элементы: системы уведомлений, отправляющей электронные письма и SMS-сообщения, игры, обновляющей разные типы противников, или обработчика документов, работающего с несколькими форматами файлов. Коллекцию не интересуют конкретные типы: важно лишь, чтобы каждый элемент предоставлял требуемое поведение.

challenge icon

Задание

Легко

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

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

  • task.go: Define интерфейс Task, который требует method Summary() string. Этот интерфейс будет общим контрактом, которому must fulfill все типы задач.
  • types.go: Create три разных типа задач, каждый из которых Implement интерфейс Task:
    • BugFix с полями ID (string) и Severity (string). Его Summary() возвращает Bug #[ID] ([Severity])
    • Feature с полями Name (string) и Points (int). Его Summary() возвращает Feature: [Name] - [Points] pts
    • Documentation с полем Topic (string). Его Summary() возвращает Docs: [Topic]
  • main.go: Create function с именем PrintBacklog, которая accepts срез Task и печатает сводку каждой задачи в отдельной строке. Считайте сведения о задачах из input, создайте по одной задаче каждого типа, соберите их all в один срез []Task и передайте его в PrintBacklog.

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

  • Строка 1: идентификатор Bug
  • Строка 2: важность Bug
  • Строка 3: имя Feature
  • Строка 4: количество Points для Feature (целое число)
  • Строка 5: тема Documentation

Например, если заданы 1042, critical, Dark Mode, 8 и API Reference, результатом должен быть:

Bug #1042 (critical)
Feature: Dark Mode - 8 pts
Docs: API Reference

Преимущество в том, что PrintBacklog не нуждается в отдельной логике для Bug, Feature или Documentation. Она просто проходит по срезу и вызывает Summary() для каждого элемента. Каждый тип задачи отвечает в собственном уникальном формате, демонстрируя, как полиморфные коллекции позволяют единообразно обрабатывать различные типы.

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

package main

import (
	"fmt"
)

// TODO: Создайте функцию PrintBacklog, которая принимает срез Task
// и выводит summary каждой задачи на отдельной строке

func main() {
	// Читаем ввод
	var bugID string
	var bugSeverity string
	var featureName string
	var featurePoints int
	var docTopic string

	fmt.Scanln(&bugID)
	fmt.Scanln(&bugSeverity)
	fmt.Scanln(&featureName)
	fmt.Scanln(&featurePoints)
	fmt.Scanln(&docTopic)

	// TODO: Создайте по одному экземпляру каждого типа задачи (BugFix, Feature, Documentation)

	// TODO: Соберите все задачи в один срез []Task

	// TODO: Вызовите PrintBacklog со срезом задач
}
quiz iconПроверьте себя

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

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

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