Паттерн Factory
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 87 из 107.
Паттерн «Фабрика» делегирует создание объектов отдельной функции или методу, позволяя создавать экземпляры без раскрытия логики создания. В отличие от Singleton, который контролирует количество экземпляров, Factory фокусируется на гибком создании объектов на основе входных параметров.
В Go фабрика обычно представляет собой функцию, которая возвращает интерфейсный тип, позволяя создавать различные конкретные типы через единый API:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}Фабричная функция NewNotifier инкапсулирует решение о том, какой конкретный тип создать. Клиентский код работает только с интерфейсом Notifier, оставаясь в неведении относительно конкретной реализации:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"Этот паттерн особенно эффективен, когда вам нужно создавать объекты на основе условий времени выполнения, вы хотите централизовать сложную логику инициализации или вам необходимо легко добавлять новые типы без изменения клиентского кода. Просто добавьте новую структуру (struct), реализующую интерфейс, и обновите фабричную функцию.
Задание
ЛегкоДавайте создадим систему конвертации документов, используя паттерн «Фабрика»! Вы создадите фабрику, которая производит различные конвертеры документов в зависимости от целевого формата, позволяя клиентскому коду работать с любым конвертером через единый интерфейс.
Вы организуете свой код в двух файлах:
converter.go: Определите интерфейс конвертера и его конкретные реализации.Создайте интерфейс
Converterс методомConvert(content string) string, который принимает содержимое документа и возвращает результат конвертации.Реализуйте три типа конвертеров:
PDFConverter— его методConvertвозвращает"PDF: " + contentHTMLConverter— его методConvertвозвращает"HTML: <p>" + content + "</p>"MarkdownConverter— его методConvertвозвращает"MD: # " + content
Создайте фабричную функцию
NewConverter(format string) Converter, которая возвращает соответствующий конвертер на основе строки формата ("pdf","html"или"markdown"). Для любого нераспознанного формата возвращайтеPDFConverterпо умолчанию.main.go: Используйте вашу фабрику для создания конвертеров и обработки документов.Считайте тип формата и содержимое документа. Используйте фабрику для создания подходящего конвертера, затем сконвертируйте содержимое и выведите результат.
Будут предоставлены следующие входные данные:
- Строка 1: Тип формата (
"pdf","html","markdown"или что-то другое) - Строка 2: Содержимое документа для конвертации
Например, если дано:
html
Welcome to Go ProgrammingВаш вывод должен быть:
HTML: <p>Welcome to Go Programming</p>И если дано:
markdown
Chapter OneВаш вывод должен быть:
MD: # Chapter OneИ если дано:
pdf
Annual Report 2024Ваш вывод должен быть:
PDF: Annual Report 2024И если дано:
docx
Meeting NotesВаш вывод должен быть:
PDF: Meeting NotesОбратите внимание, что основному коду не нужно знать о конкретных типах конвертеров — он просто запрашивает конвертер у фабрики и использует интерфейс. Это позволяет легко добавлять новые форматы в будущем без изменения клиентского кода!
Шпаргалка
Паттерн «Фабрика» (Factory pattern) делегирует создание объектов отдельной функции, позволяя создавать экземпляры без раскрытия логики создания. Он ориентирован на гибкое создание объектов на основе входных параметров.
Фабричная функция возвращает интерфейсный тип, что позволяет создавать различные конкретные типы через унифицированный API:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}Клиентский код работает только с интерфейсом, оставаясь в неведении о конкретных реализациях:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"Этот паттерн полезен, когда вам нужно создавать объекты на основе условий времени выполнения, вы хотите централизовать сложную логику инициализации или вам нужно добавлять новые типы без изменения клиентского кода.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Чтение типа формата
scanner.Scan()
format := scanner.Text()
// Чтение содержимого документа
scanner.Scan()
content := scanner.Text()
// TODO: Используйте фабрику для создания подходящего конвертера
// TODO: Преобразуйте содержимое и выведите результат
fmt.Println(result)
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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