Введение в паттерны проектирования
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 85 из 107.
Паттерны проектирования — это повторно используемые решения распространённых проблем, возникающих при проектировании программного обеспечения. Это не код, который вы копируете напрямую, а шаблоны, подсказывающие, как структурировать типы и их взаимодействие для решения конкретных задач.
Шаблоны проектирования обычно делятся на три категории:
| Категория | Назначение | Примеры |
|---|---|---|
| Порождающие | Механизмы создания объектов | Singleton, Factory, Builder |
| Структурные | Объединение типов в более крупные структуры | Adapter, Decorator, Composite |
| Поведенческие | Взаимодействие между объектами | Observer, Strategy, Command |
В Go шаблоны проектирования выглядят иначе, чем в традиционных ООП-языках.
Без классов и наследования Go реализует шаблоны с помощью interfaces, встраивания структур и композиции. Например, там, где Java мог бы использовать абстрактный класс, Go использует interface. Там, где C++ мог бы использовать наследование, Go использует встраивание.
Изучение паттернов помогает распознавать распространённые проблемы и эффективно обсуждать решения с другими разработчиками. Когда кто-то говорит: «используй здесь паттерн Strategy», вся команда понимает подход без долгих объяснений.
В следующих уроках ты реализуешь несколько фундаментальных паттернов в Go, начиная с порождающих паттернов, таких как Singleton и Factory, а затем перейдёшь к поведенческим паттернам, таким как Observer и Strategy.
Задание
ЛегкоДавайте создадим простую систему уведомлений, демонстрирующую, как шаблоны проектирования помогают организовать код! Вы создадите структуру, которую в будущих уроках можно будет легко расширить с помощью таких шаблонов, как Observer или Strategy, но сейчас сосредоточьтесь на настройке понятных interfaces и типов, соответствующих хорошим принципам ООП.
Вы организуете код в трёх файлах:
notifier.go: Define основной interface и типы для вашей системы уведомлений.Create interface
Notifierс единственным методомSend(message string) string, который returns сообщение подтверждения.Implement два конкретных типа notifier:
EmailNotifierс полемAddress. Его методSendreturnsEmail to [address]: [message]SMSNotifierс полемPhoneNumber. Его методSendreturnsSMS to [phone]: [message]
dispatcher.go: Create dispatcher, работающий с любым notifier через interface.Создайте структуру
NotificationDispatcher, которая holds срез interfacesNotifier. Add следующие методы:AddNotifier(n Notifier)для регистрации notifierBroadcast(message string) []string, который отправляет message через all зарегистрированные notifiers и returns срез всех строк подтверждения
Также create конструктор
NewNotificationDispatcher(), который returns initialized dispatcher.main.go: Настройте notifiers и транслируйте messages.Read количество notifiers, которые нужно создать. Для каждого notifier read его тип (
emailилиsms) и его destination (address или phone number). Add каждый из них в dispatcher.Затем read message для broadcast. Отправьте его через all notifiers и print каждое подтверждение в отдельной строке.
Будут предоставлены следующие входные данные:
- Строка 1: number notifiers
- Для каждого notifier: тип (
emailилиsms), затем destination - Последняя строка: message для broadcast
Например, при следующих данных:
3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnightВаш вывод должен быть таким:
Email to alice@example.com: Server maintenance at midnight
SMS to 555-1234: Server maintenance at midnight
Email to bob@example.com: Server maintenance at midnightА при следующих данных:
2
sms
555-9999
sms
555-0000
Meeting canceledВаш вывод должен быть таким:
SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceledЭта структура демонстрирует, как interfaces обеспечивают гибкие и расширяемые designs. Dispatcher не знает о конкретных типах notifier. Он просто знает, что они могут выполнять Send. Это основа, на которой строятся такие шаблоны, как Observer и Strategy!
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Прочитать количество нотификаторов
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Создать новый диспетчер
dispatcher := NewNotificationDispatcher()
// Прочитать каждый нотификатор и добавить в диспетчер
for i := 0; i < count; i++ {
notifierType, _ := reader.ReadString('\n')
notifierType = strings.TrimSpace(notifierType)
destination, _ := reader.ReadString('\n')
destination = strings.TrimSpace(destination)
// TODO: Создать соответствующий нотификатор на основе типа
// и добавить его в диспетчер
// Hint: Проверьте, является ли notifierType "email" или "sms"
}
// Прочитать сообщение для рассылки
message, _ := reader.ReadString('\n')
message = strings.TrimSpace(message)
// TODO: Разослать сообщение и вывести каждое подтверждение
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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