Полиморфные коллекции
Часть раздела Объектно-ориентированное программирование путешествия по 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-сообщения, игры, обновляющей разные типы противников, или обработчика документов, работающего с несколькими форматами файлов. Коллекцию не интересуют конкретные типы: важно лишь, чтобы каждый элемент предоставлял требуемое поведение.
Задание
ЛегкоДавайте создадим систему управления задачами, которая обрабатывает разные типы задач через единый интерфейс. Вы создадите различные типы задач и обработаете их все вместе в полиморфной коллекции.
Вы организуете код в трёх файлах:
task.go: Define интерфейсTask, который требует methodSummary() 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] ptsDocumentationс полем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 со срезом задач
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker PoolПотренируйтесь самостоятельно: Онлайн-компилятор Go