Паттерн Abstract Factory
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 88 из 107.
Паттерн Абстрактная фабрика (Abstract Factory) расширяет паттерн Фабрика (Factory), создавая семейства связанных объектов без указания их конкретных типов. Вместо одной фабричной функции вы определяете интерфейс фабрики, который реализуют несколько конкретных фабрик, каждая из которых производит согласованный набор связанных объектов.
Рассмотрим инструментарий пользовательского интерфейса (UI toolkit), которому необходимо создавать кнопки и флажки. Разные операционные системы требуют разных реализаций, но компоненты должны работать вместе согласованно:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// Интерфейс абстрактной фабрики
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Каждая конкретная фабрика производит семейство связанных компонентов:
// Семейство Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Семейство Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }Клиентский код работает с интерфейсом фабрики, оставаясь независимым от конкретных реализаций:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Используйте паттерн «Абстрактная фабрика» (Abstract Factory), когда вашей системе необходимо работать с несколькими семействами связанных продуктов и вы хотите гарантировать, что компоненты из одного и того же семейства используются вместе. Он сложнее, чем простая «Фабрика» (Factory), поэтому прибегайте к нему только тогда, когда вам действительно необходимо управлять семействами продуктов.
Задание
ЛегкоДавайте создадим систему мебельного магазина, используя паттерн Абстрактная фабрика! Вы создадите фабрики, которые производят семейства связанных предметов мебели — каждая фабрика производит мебель в определенном стиле (Modern или Vintage), гарантируя, что стулья и столы от одной и той же фабрики эстетически соответствуют друг другу.
Вы организуете свой код в трех файлах:
furniture.go: Определите интерфейсы продуктов и конкретные типы мебели.Создайте два интерфейса продуктов:
Chairс методомSitOn() stringTableс методомPlaceItems() string
Реализуйте два семейства мебели:
- Семейство Modern:
ModernChairвозвращает"Sitting on a sleek modern chair", аModernTableвозвращает"Placing items on a glass modern table" - Семейство Vintage:
VintageChairвозвращает"Sitting on an ornate vintage chair", аVintageTableвозвращает"Placing items on a wooden vintage table"
factory.go: Определите интерфейс абстрактной фабрики и конкретные фабрики.Создайте интерфейс
FurnitureFactoryс двумя методами:CreateChair() ChairCreateTable() Table
Реализуйте две конкретные фабрики:
ModernFactory, которая производитModernChairиModernTableVintageFactory, которая производитVintageChairиVintageTable
Также создайте функцию
GetFactory(style string) FurnitureFactory, которая возвращает соответствующую фабрику в зависимости от стиля ("modern"или"vintage"). По умолчанию используйтеModernFactoryдля нераспознанных стилей.main.go: Используйте свои фабрики для меблировки комнат.Создайте функцию
FurnishRoom(factory FurnitureFactory) string, которая использует фабрику для создания стула и стола, а затем возвращает отформатированную строку, показывающую оба действия на отдельных строках.Считайте входные данные стиля, получите соответствующую фабрику, обставьте комнату и выведите результат.
Будут предоставлены следующие входные данные:
- Строка 1: Стиль мебели (
"modern","vintage"или что-то другое)
Например, при вводе:
modernВаш вывод должен быть:
Sitting on a sleek modern chair
Placing items on a glass modern tableИ при вводе:
vintageВаш вывод должен быть:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableИ при вводе:
rusticВаш вывод должен быть:
Sitting on a sleek modern chair
Placing items on a glass modern tableОбратите внимание, как функция FurnishRoom работает с любой фабрикой через интерфейс — она не знает, создает ли она современную или винтажную мебель. Это гарантирует, что вся мебель в комнате принадлежит к одному семейству, сохраняя единый стиль!
Шпаргалка
Паттерн Абстрактная фабрика (Abstract Factory) создает семейства связанных объектов, не специфицируя их конкретные типы. Он определяет интерфейс фабрики, который реализуют несколько конкретных фабрик, каждая из которых производит согласованный набор связанных объектов.
Структура
Определите интерфейсы продуктов для каждого типа объектов:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}Создайте интерфейс абстрактной фабрики, который объявляет методы для создания каждого типа продукта:
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Конкретные реализации
Реализуйте конкретные семейства продуктов, где каждое семейство имеет согласованный стиль или поведение:
// Windows family
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Mac family
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }Клиентский код
Клиентский код работает с интерфейсом фабрики, оставаясь независимым от конкретных реализаций:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Когда использовать
Используйте Абстрактную фабрику, когда вашей системе необходимо работать с несколькими семействами связанных продуктов и вы хотите гарантировать, что компоненты из одного и того же семейства используются вместе. Этот паттерн сложнее, чем простая Фабрика, поэтому используйте его только тогда, когда вам действительно нужно управлять семействами продуктов.
Попробуйте сами
package main
import "fmt"
// FurnishRoom использует фабрику для создания мебели и возвращает результат
// TODO: Реализовать эту функцию
// - Использовать фабрику для создания стула и стола
// - Вернуть строку с результатом SitOn() на первой строке и результатом PlaceItems() на второй строке
func FurnishRoom(factory FurnitureFactory) string {
// TODO: Создать стул и стол, используя фабрику
// TODO: Вернуть отформатированную строку с обоими действиями на отдельных строках
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: Получить соответствующую фабрику на основе стиля
// TODO: Обставить комнату, используя фабрику
// 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