Что это такое
Паттерн Абстрактная фабрика создаёт целые семейства связанных объектов, которые принадлежат друг другу. Там, где фабричный метод создаёт один продукт, абстрактная фабрика создаёт согласованный набор — и гарантирует, что части никогда не перемешаются.
Классический случай — оформление: светлая тема должна создавать светлые кнопки и светлые метки, но никогда не смесь.
Пример: фабрика тем
renderScreen принимает любую UIFactory и строит из неё согласованный экран. Смена всего внешнего вида — это один аргумент, а смешать темы невозможно по построению.
Одна фабрика — одно согласованное семейство
Светлая тема создаёт только светлые части, тёмная — только тёмные. renderScreen при этом не меняется.
Вывод: программа на Go и захваченный вывод
package main
import "fmt"
type Button interface{ Render() string }
type Label interface{ Render() string }
// Абстрактная фабрика создаёт целое согласованное семейство.
type UIFactory interface {
NewButton() Button
NewLabel() Label
}
type LightButton struct{}
func (LightButton) Render() string { return "[ OK ] светлая кнопка" }
type LightLabel struct{}
func (LightLabel) Render() string { return "привет светлая метка" }
type DarkButton struct{}
func (DarkButton) Render() string { return "[ OK ] тёмная кнопка" }
type DarkLabel struct{}
func (DarkLabel) Render() string { return "привет тёмная метка" }
type LightTheme struct{}
func (LightTheme) NewButton() Button { return LightButton{} }
func (LightTheme) NewLabel() Label { return LightLabel{} }
type DarkTheme struct{}
func (DarkTheme) NewButton() Button { return DarkButton{} }
func (DarkTheme) NewLabel() Label { return DarkLabel{} }
func renderScreen(f UIFactory) {
fmt.Println(f.NewLabel().Render())
fmt.Println(f.NewButton().Render())
}
func main() {
renderScreen(LightTheme{})
renderScreen(DarkTheme{})
}
Когда её применять
Применяйте, когда несколько объектов должны оставаться согласованными друг с другом — темы, наборы виджетов для разных платформ, парные форматы импорта и экспорта.
Не применяйте, когда продукты не обязаны совпадать. Независимым объектам нужны независимые конструкторы, а не фабрика семейства.
Плюсы и минусы
Плюсы: согласованность семейства гарантирована; целые семейства заменяются одним значением; вызывающий код зависит только от интерфейсов.
Минусы: много интерфейсов и типов для небольшой задачи; добавление нового продукта в семейство требует правки каждой фабрики.