Паттерн Abstract Factory
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 88 из 107.
Паттерн Abstract Factory расширяет паттерн factory, создавая семейства связанных объектов без указания их конкретных типов. Вместо одной factory function вы определяете factory interface, который реализуют несколько конкретных factory, каждая из которых создаёт согласованный набор связанных объектов.
Рассмотрим набор инструментов пользовательского интерфейса, которому необходимо создавать кнопки и флажки. Разным операционным системам требуются разные реализации, но компоненты должны согласованно работать вместе:
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{} }Клиентский код работает с интерфейсом factory, оставаясь независимым от конкретных реализаций:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Используйте Abstract Factory, когда вашей системе необходимо работать с несколькими семействами связанных продуктов и вы хотите гарантировать совместное использование компонентов из одного семейства. Он сложнее, чем простой Factory, поэтому обращайтесь к нему только тогда, когда вам действительно нужно управлять семействами продуктов.
Задание
ЛегкоДавайте создадим систему мебельного магазина с использованием паттерна Abstract Factory! Вы создадите фабрики, которые производят семейства связанных предметов мебели: каждая фабрика производит единый стиль (Modern или Vintage), обеспечивая эстетическое соответствие стульев и столов, созданных одной фабрикой.
Вы организуете свой код в трёх файлах:
furniture.go: Define интерфейсы продуктов и конкретные типы мебели.Создайте два интерфейса продуктов:
Chairс методомSitOn() stringTableс методомPlaceItems() string
Реализуйте два семейства мебели:
- Modern family:
ModernChairвозвращаетSitting on a sleek modern chair, аModernTableвозвращаетPlacing items on a glass modern table - Vintage family:
VintageChairвозвращаетSitting on an ornate vintage chair, аVintageTableвозвращаетPlacing items on a wooden vintage table
factory.go: Define ваш интерфейс абстрактной фабрики и конкретные фабрики.Создайте интерфейс
FurnitureFactoryс двумя методами:CreateChair() ChairCreateTable() Table
Реализуйте две конкретные фабрики:
ModernFactory, которая производитModernChairиModernTableVintageFactory, которая производитVintageChairиVintageTable
Также создайте функцию
GetFactory(style string) FurnitureFactory, которая возвращает appropriate фабрику на основе стиля (modernилиvintage). Для нераспознанных стилей используйтеModernFactoryпо умолчанию.main.go: Используйте свои фабрики, чтобы обставлять комнаты.Создайте функцию
FurnishRoom(factory FurnitureFactory) string, которая использует фабрику для создания стула и стола, а затем возвращает formatted строку, отображающую оба действия в отдельных строках.Считайте введённый стиль, получите appropriate фабрику, обставьте комнату и выведите результат.
Будут предоставлены следующие входные данные:
- Строка 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 работает с любой фабрикой через интерфейс. Она не знает, создаёт ли modern или vintage мебель. Это гарантирует, что вся мебель в комнате поступает из одного family, сохраняя единый стиль!
Попробуйте сами
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Потренируйтесь самостоятельно: Онлайн-компилятор Go