Паттерн Строитель в Python
Паттерн Строитель позволяет создавать различные представления объектов, используя один и тот же процесс конструирования. Представьте, что вы строите дом: строите ли вы виллу или многоквартирный дом, нужно пройти такие этапы, как закладка фундамента, возведение стен, установка сантехники и электропроводки, но конечные результаты совершенно разные.
Ключевая идея
Паттерн Строитель отделяет процесс конструирования сложного объекта от его представления, позволяя одному и тому же процессу конструирования создавать разные представления. Этот паттерн особенно подходит для создания сложных объектов, состоящих из нескольких компонентов.
Зачем нам нужен паттерн Строитель?
Проблемы традиционных методов конструирования
Сначала рассмотрим пример, в котором не используется паттерн Строитель:
Пример
def __init__(self, cpu, memory, storage, graphics_card, monitor):
self.cpu = cpu
self.memory = memory
self.storage = storage
self.graphics_card = graphics_card
self.monitor = monitor
def __str__(self):
return f"Компьютер: CPU={self.cpu}, Память={self.memory}GB, Хранилище={self.storage}GB, Графика={self.graphics_card}, Монитор={self.monitor}"
# Создать объект компьютера
computer = Computer("Intel i7", 16, 512, "NVIDIA RTX 3060", "27-дюймовый 4K")
print(computer)
Недостатки этого подхода:
- Слишком много параметров конструктора, что затрудняет сопровождение
- Приходится запоминать порядок всех параметров
- Процесс создания недостаточно гибок
- Плохая читаемость кода
Компоненты паттерна «Строитель»
Паттерн «Строитель» включает четыре основные роли:

1. Продукт
Сложный объект, который нужно создать
2. Builder (Абстрактный строитель)
Определяет абстрактный интерфейс для создания различных частей продукта
3. ConcreteBuilder (Конкретный строитель)
Реализует интерфейс Builder, конструируя и собирая различные части
4. Директор
Создает объект, использующий интерфейс Builder
Полная реализация кода
Давайте полностью реализуем паттерн Builder на примере конфигурации компьютера:
1. Класс продукта
Пример
"""Класс продукта Computer"""
def __init__(self):
self.cpu = None
self.memory = None
self.storage = None
self.graphics_card = None
self.monitor = None
def __str__(self):
specs = []
if self.cpu:
specs.append(f"Процессор: {self.cpu}")
if self.memory:
specs.append(f"Память: {self.memory}GB")
if self.storage:
specs.append(f"Накопитель: {self.storage}GB")
if self.graphics_card:
specs.append(f"Видеокарта: {self.graphics_card}")
if self.monitor:
specs.append(f"Монитор: {self.monitor}")
return "Конфигурация компьютера:\n" + "\n".join(f" - {spec}" for spec in specs)
2. Абстрактный строитель (Builder)
Пример
class ComputerBuilder(ABC):
"""Абстрактный класс строителя компьютера"""
def __init__(self):
self.computer = Computer()
@abstractmethod
def build_cpu(self):
pass
@abstractmethod
def build_memory(self):
pass
@abstractmethod
def build_storage(self):
pass
@abstractmethod
def build_graphics_card(self):
pass
@abstractmethod
def build_monitor(self):
pass
def get_computer(self):
return self.computer
3. ConcreteBuilder (Конкретный строитель)
Пример
"""Строитель игрового компьютера"""
def build_cpu(self):
self.computer.cpu = "Intel i9-13900K"
def build_memory(self):
self.computer.memory = 32
def build_storage(self):
self.computer.storage = 2000 # 2TB
def build_graphics_card(self):
self.computer.graphics_card = "NVIDIA RTX 4090"
def build_monitor(self):
self.computer.monitor = "32-inch 4K 144Hz"
class OfficeComputerBuilder(ComputerBuilder):
"""Строитель офисных компьютеров"""
def build_cpu(self):
self.computer.cpu = "Intel i5-13400"
def build_memory(self):
self.computer.memory = 16
def build_storage(self):
self.computer.storage = 512
def build_graphics_card(self):
self.computer.graphics_card = "Встроенная графика"
def build_monitor(self):
self.computer.monitor = "24-inch 1080P"
4. Директор
Пример
"""Директор по сборке компьютеров"""
def __init__(self, builder):
self.builder = builder
def construct_computer(self):
"""Полный процесс сборки компьютера"""
self.builder.build_cpu()
self.builder.build_memory()
self.builder.build_storage()
self.builder.build_graphics_card()
self.builder.build_monitor()
def get_computer(self):
return self.builder.get_computer()
Пример использования
Теперь давайте посмотрим, как использовать паттерн «Строитель» для создания различных типов компьютеров:
Пример
print("=== Демонстрация паттерна Строитель ===\n\n")
# Создание игрового компьютера
print("1. Сборка игрового компьютера:")
gaming_builder = GamingComputerBuilder()
director = ComputerDirector(gaming_builder)
director.construct_computer()
gaming_computer = director.get_computer()
print(gaming_computer)
print("\n" + "="*50 + "\n")
# Создание офисного компьютера
print("2. Сборка офисного компьютера:")
office_builder = OfficeComputerBuilder()
director = ComputerDirector(office_builder)
director.construct_computer()
office_computer = director.get_computer()
print(office_computer)
print("\n" + "="*50 + "\n")
# Создать пользовательский компьютер
print("3. Сборка пользовательского компьютера:")
custom_builder = CustomComputerBuilder()
custom_builder.build_cpu("AMD Ryzen 7 7800X3D")
custom_builder.build_memory(64)
custom_builder.build_storage(4000)
custom_builder.build_graphics_card("AMD RX 7900 XTX")
custom_builder.build_monitor("34-inch изогнутый ультраширокий монитор")
custom_computer = custom_builder.get_computer()
print(custom_computer)
# Пользовательский строитель (сборка с необязательными шагами)
class CustomComputerBuilder:
"""Пользовательский строитель компьютера"""
def __init__(self):
self.computer = Computer()
def build_cpu(self, cpu):
self.computer.cpu = cpu
return self # Вернуть self для поддержки цепочек вызовов
def build_memory(self, memory):
self.computer.memory = memory
return self
def build_storage(self, storage):
self.computer.storage = storage
return self
def build_graphics_card(self, graphics_card):
self.computer.graphics_card = graphics_card
return self
def build_monitor(self, monitor):
self.computer.monitor = monitor
return self
def get_computer(self):
return self.computer
if __name__ == "__main__":
main()
Варианты паттерна «Строитель»
1. Строитель с текучим интерфейсом
Пример
def __init__(self):
self.size = None
self.cheese = False
self.pepperoni = False
self.mushrooms = False
def __str__(self):
ingredients = []
if self.cheese: ingredients.append("Cheese")
if self.pepperoni: ingredients.append("Italian sausage")
if self.mushrooms: ingredients.append("Грибы")
return f"{self.size}-inch пицца, начинки: {', '.join(ingredients)}"
class PizzaBuilder:
def __init__(self, size):
self.pizza = Pizza()
self.pizza.size = size
def add_cheese(self):
self.pizza.cheese = True
return self
def add_pepperoni(self):
self.pizza.pepperoni = True
return self
def add_mushrooms(self):
self.pizza.mushrooms = True
return self
def build(self):
return self.pizza
# Используйте fluent-интерфейс
pizza = (PizzaBuilder(12)
.add_cheese()
.add_pepperoni()
.add_mushrooms()
.build())
print(pizza)
Плюсы и минусы паттерна «Строитель»
Преимущества
| Преимущества | Описание |
|---|---|
| Хорошая инкапсуляция | Процесс конструирования отделён от представления; клиентам не нужно знать внутренние детали. |
| Высокая расширяемость | Новые конкретные строители можно удобно добавлять. |
| Лучший контроль | Позволяет более тонко управлять процессом конструирования. |
| Читаемость кода | Код становится понятнее при использовании цепочечных вызовов. |
Недостатки
| Недостатки | Описание |
|---|---|
| Добавляет сложность | Требует определения нескольких классов, что увеличивает сложность системы. |
| Не подходит, когда продукты сильно различаются | Если продукты сильно различаются между собой, паттерн «Строитель» подходит не очень хорошо. |
| Строители зависят от продукта | Строителю нужно знать конкретные детали продукта. |
Применимые сценарии
Случаи, подходящие для паттерна «Строитель»:
- Создание сложных объектов: нужно создавать сложные объекты, состоящие из нескольких частей.
- Стабильный процесс конструирования: процесс конструирования относительно стабилен, но компоненты объекта часто меняются.
- Множественные представления: создаваемый объект имеет несколько различных представлений.
- Избегайте перекрывающихся конструкторов: избегайте использования конструкторов с большим количеством параметров.
Примеры практического применения:
- Конвертер документов: преобразование документов в различные форматы, такие как PDF, Word, HTML.
- Приготовление еды: приготовление блюд для разных наборов (комбинаций бургеров, картофеля фри, напитков).
- Создание компонентов интерфейса: построение сложных компонентов пользовательского интерфейса.
- Генерация отчётов: генерация отчётов, содержащих различные части (заголовок, данные, диаграммы).
Практические упражнения
Упражнение 1: Реализуйте строитель автомобиля
Попробуйте реализовать паттерн «строитель» для автомобиля со следующими компонентами:
- Двигатель (бензиновый, электрический, гибридный)
- Цвет кузова (красный, синий, чёрный, белый)
- Размер колёс (16 дюймов, 18 дюймов, 20 дюймов)
- Интерьер (кожа, ткань)
Упражнение 2: Улучшите конструктор компьютера
Добавьте следующие возможности в конструктор компьютера:
- Дополнительные компоненты (например, устанавливать ли оптический привод)
- Логика проверки (например, объём памяти не может быть меньше 4GB)
- Функция расчёта цены
Итог
Паттерн Строитель — это мощный порождающий паттерн проектирования. Разбивая процесс конструирования сложного объекта на несколько простых шагов, он делает процесс создания более гибким и управляемым. Хотя он увеличивает сложность системы, в сценариях, где нужно создавать сложные объекты и процесс конструирования относительно фиксирован, паттерн Строитель может значительно повысить сопровождаемость и масштабируемость кода.
Помните основную идею паттернов проектирования:Не используйте паттерны ради использования паттернов, а для решения конкретных задач проектирования.В реальной разработке решайте, использовать ли паттерн Строитель, исходя из конкретных требований.
Другие расширения