Затенение встроенных методов
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 39 из 107.
Иногда нужно, чтобы внешняя структура предоставляла собственную реализацию метода, который уже есть у встроенного типа. Это называется затенением и позволяет переопределять продвигаемые методы с помощью пользовательского поведения.
Когда вы определяете method во внешней структуре с тем же именем, что и embedded method, внешний method имеет приоритет:
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "Some sound"
}
type Dog struct {
Animal
Breed string
}
func (d Dog) Speak() string {
return "Woof!"
}
Теперь, когда вы вызываете Speak() для Dog, используется внешний метод:
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof! - метод Dog
fmt.Println(d.Animal.Speak()) // Какой-то звук - метод Animal
}
embedded метод не исчез. Ты всё ещё можешь явно получить к нему доступ через имя embedded типа. Этот шаблон полезен, когда ты хочешь настроить поведение, сохранив доступной исходную реализацию, например чтобы вызвать её из переопределяющего метода:
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
Скрытие позволяет контролировать, какое поведение предоставляется, сохраняя доступ к исходному через явную квалификацию.
Задание
ЛегкоДавайте создадим систему уведомлений, в которой разные каналы уведомлений могут настраивать способ форматирования и доставки сообщений. Вы будете использовать затенение методов, чтобы отдельные каналы могли переопределять поведение по умолчанию, сохраняя при этом возможность обращаться к исходной реализации при необходимости.
Вы организуете код в трёх файлах:
base.go: Создайте структуруBaseNotificationс полямиTitleиPriority(both строкового типа). Добавьте ей методFormat() string, который возвращает notification в стандартном формате:[Priority] Title.channels.go: Создайте два типа каналов уведомлений, которые встраиваютBaseNotificationи затеняют методFormatсобственными реализациями:EmailNotificationс полемRecipient. Его методFormat()должен возвращатьEmail to [Recipient]: [Priority] TitleSlackNotificationс полемChannel. Его методFormat()должен вызывать встроенный методBaseNotification.Format()и добавлять#[Channel]:в начало результата
main.go: Считайте сведения об уведомлении из входных данных, создайте оба типа уведомлений и продемонстрируйте затенение, выведя:- отформатированный output
EmailNotification(с использованием его затенённого метода) - базовый формат
EmailNotification(явно вызывая встроенный метод) - отформатированный output
SlackNotification(который внутри использует базовый метод)
- отформатированный output
Будут предоставлены следующие входные данные:
- Строка 1: Заголовок notification
- Строка 2: Уровень priority
- Строка 3: Получатель email
- Строка 4: Имя канала Slack
Например, если заданы Server Alert, HIGH, admin@company.com и ops-alerts, output должен быть таким:
Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server AlertОбратите внимание, как EmailNotification полностью заменяет базовое форматирование, тогда как SlackNotification расширяет его, вызывая исходный метод и добавляя контекст канала. Оба подхода демонстрируют разные способы эффективного использования затенения методов.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Читать заголовок уведомления
scanner.Scan()
title := scanner.Text()
// Читать уровень приоритета
scanner.Scan()
priority := scanner.Text()
// Читать получателя email
scanner.Scan()
recipient := scanner.Text()
// Читать имя канала Slack
scanner.Scan()
channel := scanner.Text()
// TODO: Создать EmailNotification с базовым уведомлением, title, priority и recipient
// TODO: Создать SlackNotification с базовым уведомлением, title, priority и channel
// TODO: Вывести форматированный вывод EmailNotification (используя его затенённый метод)
// TODO: Вывести базовый формат EmailNotification (явно вызывая встроенный метод)
// TODO: Вывести форматированный вывод SlackNotification
fmt.Println() // Заглушка - удалить при реализации
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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