Menu
Coddy logo textTech

Введение в паттерны проектирования

Часть раздела Объектно-ориентированное программирование путешествия по 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.

challenge icon

Задание

Легко

Давайте создадим простую систему уведомлений, демонстрирующую, как шаблоны проектирования помогают организовать код! Вы создадите структуру, которую в будущих уроках можно будет легко расширить с помощью таких шаблонов, как Observer или Strategy, но сейчас сосредоточьтесь на настройке понятных interfaces и типов, соответствующих хорошим принципам ООП.

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

  • notifier.go: Define основной interface и типы для вашей системы уведомлений.

    Create interface Notifier с единственным методом Send(message string) string, который returns сообщение подтверждения.

    Implement два конкретных типа notifier:

    • EmailNotifier с полем Address. Его метод Send returns Email to [address]: [message]
    • SMSNotifier с полем PhoneNumber. Его метод Send returns SMS to [phone]: [message]
  • dispatcher.go: Create dispatcher, работающий с любым notifier через interface.

    Создайте структуру NotificationDispatcher, которая holds срез interfaces Notifier. Add следующие методы:

    • AddNotifier(n Notifier) для регистрации notifier
    • Broadcast(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: Разослать сообщение и вывести каждое подтверждение

}
quiz iconПроверьте себя

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

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

13Паттерны проектирования. Часть 1

Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy

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