← Назад к вопросам

Какие знаешь паттерны ООП?

1.8 Middle🔥 191 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерны ООП (Design Patterns)

За 10+ лет разработки я использовал много паттернов проектирования. Они помогают решить общие проблемы в коде. Я разделю их на три категории: Creational, Structural, Behavioral.

Creational Patterns (Создание объектов)

1. Singleton

Суть: класс имеет только один экземпляр во всей программе

# Плохо: глобальная переменная
logger = None

def get_logger():
    global logger
    if logger is None:
        logger = Logger()
    return logger

# Хорошо: Singleton паттерн
class Singleton(type):
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=Singleton):
    def __init__(self):
        self.connection = connect_to_db()

db1 = Database()
db2 = Database()
print(db1 is db2)  # True

Когда использовать:

  • Database connection
  • Logger
  • Configuration manager
  • Cache

Минусы:

  • Сложно тестировать (трудно мокировать)
  • Global state (плохо для параллелизма)
  • Может быть легче заменить на Dependency Injection

2. Factory Pattern

Суть: создание объектов без указания их точного класса

# Плохо: в коде много if/else
class PaymentProcessor:
    def process(self, method, amount):
        if method == 'credit_card':
            processor = CreditCardProcessor()
        elif method == 'paypal':
            processor = PayPalProcessor()
        elif method == 'bitcoin':
            processor = BitcoinProcessor()
        else:
            raise ValueError()
        
        return processor.pay(amount)

# Хорошо: Factory паттерн
class PaymentFactory:
    _processors = {
        'credit_card': CreditCardProcessor,
        'paypal': PayPalProcessor,
        'bitcoin': BitcoinProcessor,
    }
    
    @classmethod
    def create(cls, method):
        processor_class = cls._processors.get(method)
        if not processor_class:
            raise ValueError(f"Unknown payment method: {method}")
        return processor_class()

# Использование
processor = PaymentFactory.create('paypal')
processor.pay(100)

Когда использовать:

  • Создание разных видов объектов
  • Когда класс не знает, какой подкласс создавать
  • Plugin systems

3. Builder Pattern

Суть: строишь сложный объект пошагово

# Плохо: много параметров в конструкторе
class Report:
    def __init__(self, title, author, template, date_format, 
                 include_summary, include_charts, include_footer):
        self.title = title
        self.author = author
        self.template = template
        # ... и т.д.

report = Report(
    title="Q4 Report",
    author="John",
    template="corporate",
    date_format="dd/mm/yyyy",
    include_summary=True,
    include_charts=False,
    include_footer=True
)

# Хорошо: Builder паттерн
class ReportBuilder:
    def __init__(self):
        self._title = None
        self._author = None
        self._template = None
        self._options = {}
    
    def with_title(self, title):
        self._title = title
        return self  # Для цепочки вызовов
    
    def with_author(self, author):
        self._author = author
        return self
    
    def with_template(self, template):
        self._template = template
        return self
    
    def include_summary(self, value=True):
        self._options['summary'] = value
        return self
    
    def include_charts(self, value=True):
        self._options['charts'] = value
        return self
    
    def build(self):
        return Report(
            title=self._title,
            author=self._author,
            template=self._template,
            **self._options
        )

# Использование (гораздо читабельнее!)
report = (ReportBuilder()
    .with_title("Q4 Report")
    .with_author("John")
    .with_template("corporate")
    .include_summary()
    .include_charts(False)
    .build())

Когда использовать:

  • Сложные объекты с множеством параметров
  • Хотим избежать громоздких конструкторов
  • Хотим гибкость в создании

4. Prototype Pattern

Суть: копирование объекта вместо создания нового

import copy

class UserProfile:
    def __init__(self, name, email, roles):
        self.name = name
        self.email = email
        self.roles = roles  # list
    
    def clone(self):
        return copy.deepcopy(self)

# Использование
original = UserProfile("John", "john@example.com", ["admin", "user"])
clone = original.clone()
clone.name = "Jane"  # Не влияет на original

Structural Patterns (Структура объектов)

1. Adapter Pattern

Суть: адаптируешь один интерфейс под другой

# Старый код
class LegacyPaymentGateway:
    def charge(self, amount):
        # Старый API
        pass

# Новый код ожидает другой интерфейс
class PaymentProcessor:
    def process_payment(self, amount):
        pass

# Adapter
class PaymentAdapter(PaymentProcessor):
    def __init__(self, legacy_gateway):
        self.gateway = legacy_gateway
    
    def process_payment(self, amount):
        # Адаптируем старый API под новый
        return self.gateway.charge(amount)

# Использование
legacy = LegacyPaymentGateway()
adapted = PaymentAdapter(legacy)
adapted.process_payment(100)

Когда использовать:

  • Интеграция старого и нового кода
  • Работа со сторонними library с неудобным API

2. Decorator Pattern

Суть: добавляешь новую функциональность к объекту динамически

# Базовый класс
class Coffee:
    def cost(self):
        return 5
    
    def description(self):
        return "Coffee"

# Декораторы (добавляем функциональность)
class MilkDecorator:
    def __init__(self, coffee):
        self.coffee = coffee
    
    def cost(self):
        return self.coffee.cost() + 2
    
    def description(self):
        return self.coffee.description() + ", Milk"

class SugarDecorator:
    def __init__(self, coffee):
        self.coffee = coffee
    
    def cost(self):
        return self.coffee.cost() + 0.5
    
    def description(self):
        return self.coffee.description() + ", Sugar"

# Использование
coffee = Coffee()
coffee = MilkDecorator(coffee)
coffee = SugarDecorator(coffee)
print(coffee.description())  # Coffee, Milk, Sugar
print(coffee.cost())  # 7.5

Python версия (проще):

def log_execution(func):
    def wrapper(*args, **kwargs):
        print(f"Executing {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Finished {func.__name__}")
        return result
    return wrapper

@log_execution
def expensive_operation():
    return "Result"

3. Facade Pattern

Суть: предоставляешь простой интерфейс к сложной системе

# Сложная система
class EmailService:
    def send(self, email, message): pass

class SMSService:
    def send(self, phone, message): pass

class SlackService:
    def post(self, channel, message): pass

# Facade
class NotificationFacade:
    def __init__(self):
        self.email = EmailService()
        self.sms = SMSService()
        self.slack = SlackService()
    
    def notify_user(self, user, message):
        """Простой интерфейс, скрывает сложность"""
        self.email.send(user.email, message)
        self.sms.send(user.phone, message)
        self.slack.post(f"user_{user.id}", message)

# Использование
notifier = NotificationFacade()
notifier.notify_user(user, "Hello!")

Behavioral Patterns (Поведение объектов)

1. Strategy Pattern

Суть: encapsulate алгоритм в класс, чтобы его менять

# Плохо: if/else для разных алгоритмов
class Discount:
    def calculate(self, amount, customer_type):
        if customer_type == 'regular':
            return amount * 0.1
        elif customer_type == 'vip':
            return amount * 0.2
        elif customer_type == 'gold':
            return amount * 0.3

# Хорошо: Strategy паттерн
class DiscountStrategy:
    def get_discount(self, amount):
        pass

class RegularDiscount(DiscountStrategy):
    def get_discount(self, amount):
        return amount * 0.1

class VIPDiscount(DiscountStrategy):
    def get_discount(self, amount):
        return amount * 0.2

class GoldDiscount(DiscountStrategy):
    def get_discount(self, amount):
        return amount * 0.3

class Order:
    def __init__(self, amount, strategy):
        self.amount = amount
        self.strategy = strategy
    
    def get_final_price(self):
        discount = self.strategy.get_discount(self.amount)
        return self.amount - discount

# Использование
order = Order(100, VIPDiscount())
order.get_final_price()  # 80

2. Observer Pattern

Суть: объект уведомляет других об изменении состояния

class Subject:
    def __init__(self):
        self._observers = []
    
    def attach(self, observer):
        self._observers.append(observer)
    
    def notify(self, event):
        for observer in self._observers:
            observer.update(event)

class UserRegistered:
    def __init__(self, user_id):
        self.user_id = user_id

class EmailNotifier:
    def update(self, event):
        if isinstance(event, UserRegistered):
            print(f"Send welcome email to user {event.user_id}")

class AnalyticsTracker:
    def update(self, event):
        if isinstance(event, UserRegistered):
            print(f"Track registration: {event.user_id}")

# Использование
subject = Subject()
subject.attach(EmailNotifier())
subject.attach(AnalyticsTracker())
subject.notify(UserRegistered(123))
# Output:
# Send welcome email to user 123
# Track registration: 123

3. Chain of Responsibility

Суть: передаёшь запрос по цепочке обработчиков

class Handler:
    def __init__(self, next_handler=None):
        self.next = next_handler
    
    def handle(self, request):
        if self.can_handle(request):
            return self.process(request)
        elif self.next:
            return self.next.handle(request)
        return None
    
    def can_handle(self, request):
        raise NotImplementedError
    
    def process(self, request):
        raise NotImplementedError

class AuthHandler(Handler):
    def can_handle(self, request):
        return request.needs_auth
    
    def process(self, request):
        return f"Auth: {request.path}"

class LogHandler(Handler):
    def can_handle(self, request):
        return request.should_log
    
    def process(self, request):
        return f"Log: {request.path}"

class ProcessHandler(Handler):
    def can_handle(self, request):
        return True
    
    def process(self, request):
        return f"Process: {request.path}"

# Использование
log_handler = LogHandler()
auth_handler = AuthHandler(log_handler)
process_handler = ProcessHandler(auth_handler)

result = auth_handler.handle(request)

4. State Pattern

Суть: меняешь поведение объекта в зависимости от состояния

class OrderState:
    def next(self, order):
        raise NotImplementedError

class PendingState(OrderState):
    def next(self, order):
        order.state = ConfirmedState()

class ConfirmedState(OrderState):
    def next(self, order):
        order.state = ShippedState()

class ShippedState(OrderState):
    def next(self, order):
        order.state = DeliveredState()

class Order:
    def __init__(self):
        self.state = PendingState()
    
    def proceed(self):
        self.state.next(self)
        print(f"Order state: {self.state.__class__.__name__}")

# Использование
order = Order()
order.proceed()  # Order state: ConfirmedState
order.proceed()  # Order state: ShippedState

SOLID Principles (не паттерны, но близко)

# S — Single Responsibility
# Класс должен иметь только одну причину для изменения
class User:
    def save(self): pass  # Плохо: не сохраняй сам

class UserRepository:
    def save(self, user): pass  # Хорошо: отдельный класс

# O — Open/Closed
# Открыт для расширения, закрыт для модификации
class PaymentProcessor:
    def process(self, method, amount):
        # Не добавляй новые if, используй Strategy
        return method.process(amount)

# L — Liskov Substitution
# Подтипы должны быть заменяемы базовыми типами
class Bird:
    def fly(self): pass

class Penguin(Bird):  # Плохо: пингвины не летают
    def fly(self): raise NotImplementedError

# D — Dependency Inversion
# Завись от интерфейсов, не реализаций
class UserService:
    def __init__(self, db):  # Плохо: конкретная БД
        self.db = db

class UserService:
    def __init__(self, repository):  # Хорошо: интерфейс
        self.repository = repository

Какие паттерны я использую каждый день

✅ Все время:
- Decorator (@property, @classmethod)
- Factory
- Strategy
- Observer (event listeners, callbacks)

✅ Часто:
- Builder
- Adapter
- Facade
- State

⚠️ Когда нужно:
- Singleton (Database)
- Chain of Responsibility
- Proxy
- Template Method

❌ Редко:
- Prototype (используем copy вместо)
- Interpreter
- Visitor (сложновато)

Итог: паттерны — это проверенные решения распространённых проблем. Не нужно использовать все, но нужно знать основные. Главное — решить проблему, потом подумать, какой паттерн подойдёт.