Menu
Coddy logo textTech

Паттерн 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), поэтому прибегайте к нему только тогда, когда вам действительно необходимо управлять семействами продуктов.

challenge icon

Задание

Легко

Давайте создадим систему мебельного магазина, используя паттерн Абстрактная фабрика! Вы создадите фабрики, которые производят семейства связанных предметов мебели — каждая фабрика производит мебель в определенном стиле (Modern или Vintage), гарантируя, что стулья и столы от одной и той же фабрики эстетически соответствуют друг другу.

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

  • furniture.go: Определите интерфейсы продуктов и конкретные типы мебели.

    Создайте два интерфейса продуктов:

    • Chair с методом SitOn() string
    • Table с методом 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() Chair
    • CreateTable() Table

    Реализуйте две конкретные фабрики:

    • ModernFactory, которая производит ModernChair и ModernTable
    • VintageFactory, которая производит 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: Вывести результат
}
quiz iconПроверьте себя

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

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