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

Расскажи как используешь ООП

3.0 Senior🔥 141 комментариев
#DevOps и инфраструктура#Django

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

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

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

Как я использую ООП в реальных проектах

15 лет опыта научили меня: ООП — инструмент, а не религия. Расскажу честно, как и когда я его применяю.

Принцип: ООП для моделей, функции для логики

Когда ООП ОБЯЗАТЕЛЕН

1. Моделирование реального мира

# Правильно: сущность User существует в реальности
class User:
    def __init__(self, id: int, email: str, name: str):
        self.id = id
        self.email = email
        self.name = name
    
    def is_valid_email(self) -> bool:
        return '@' in self.email
    
    def full_name(self) -> str:
        return self.name.upper()

# Использование
user = User(1, "alice@example.com", "Alice")
if user.is_valid_email():
    print(f"Hello, {user.full_name()}")

2. Состояние с методами

# Класс имеет состояние И поведение
class BankAccount:
    def __init__(self, balance: float):
        self._balance = balance
    
    def deposit(self, amount: float) -> None:
        if amount <= 0:
            raise ValueError("Сумма должна быть > 0")
        self._balance += amount
    
    def withdraw(self, amount: float) -> None:
        if amount > self._balance:
            raise ValueError("Недостаточно средств")
        self._balance -= amount
    
    @property
    def balance(self) -> float:
        return self._balance

# Использование
account = BankAccount(1000)
account.deposit(500)
account.withdraw(200)
print(account.balance)  # 1300

Когда ООП ПЛОХОЙ выбор

Ошибка 1: Класс для всего

# ❌ Плохо: избыточное ООП
class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b

result = MathUtils.add(5, 3)

# ✅ Хорошо: просто функция
def add(a, b):
    return a + b

result = add(5, 3)

Ошибка 2: Глубокая иерархия наследования

# ❌ Плохо: 5 уровней наследования
class Animal:
    pass

class Mammal(Animal):
    pass

class Carnivore(Mammal):
    pass

class Cat(Carnivore):
    pass

# ✅ Хорошо: используй композицию
class Animal:
    def __init__(self, diet: str, habitat: str):
        self.diet = diet
        self.habitat = habitat

Мой реальный стек: SOLID принципы

Single Responsibility: одна обязанность

# ❌ Плохо: класс делает слишком много
class User:
    def __init__(self, email: str):
        self.email = email
    
    def save_to_db(self):
        # Сохранение в БД
        pass
    
    def send_email_verification(self):
        # Отправка писем
        pass
    
    def calculate_premium_discount(self):
        # Бизнес-логика
        pass

# ✅ Хорошо: разделение ответственности
class User:
    def __init__(self, email: str, name: str):
        self.email = email
        self.name = name

class UserRepository:
    def save(self, user: User) -> None:
        # Только сохранение в БД
        db.insert(user)

class EmailService:
    def send_verification(self, user: User) -> None:
        # Только отправка писем
        send_email(user.email, "Verify your account")

class PricingService:
    def calculate_discount(self, user: User) -> float:
        # Только бизнес-логика скидок
        return 0.1 if user.is_premium else 0.0

Open/Closed: расширяемость без изменений

# ❌ Плохо: нужно менять класс для новых типов
class PaymentProcessor:
    def process(self, method: str, amount: float):
        if method == "credit_card":
            # Логика для кредитной карты
            pass
        elif method == "paypal":
            # Логика для PayPal
            pass
        # Каждый новый способ — изменение класса

# ✅ Хорошо: расширяемость через наследование
class PaymentMethod:
    def process(self, amount: float) -> bool:
        raise NotImplementedError

class CreditCardPayment(PaymentMethod):
    def process(self, amount: float) -> bool:
        # Логика для кредитной карты
        return True

class PayPalPayment(PaymentMethod):
    def process(self, amount: float) -> bool:
        # Логика для PayPal
        return True

class PaymentProcessor:
    def __init__(self, method: PaymentMethod):
        self.method = method
    
    def process(self, amount: float) -> bool:
        return self.method.process(amount)

# Использование
processor = PaymentProcessor(CreditCardPayment())
processor.process(100)

Dependency Injection: зависимости через конструктор

# ❌ Плохо: жёсткие зависимости
class UserService:
    def __init__(self):
        self.db = PostgreSQL()  # Жёстко привязано к PostgreSQL
        self.email = Gmail()    # Жёстко привязано к Gmail
    
    def register(self, email: str):
        self.db.insert(email)
        self.email.send("Welcome!")

# ✅ Хорошо: инъекция зависимостей
class UserService:
    def __init__(self, db: Database, email_service: EmailService):
        self.db = db
        self.email = email_service
    
    def register(self, email: str):
        self.db.insert(email)
        self.email.send("Welcome!")

# Использование
db = PostgreSQL()
email = Gmail()
service = UserService(db, email)
service.register("alice@example.com")

# Для тестов — подменяем на mock
db_mock = MockDatabase()
email_mock = MockEmailService()
service_test = UserService(db_mock, email_mock)

Мой реальный код: FastAPI приложение

from typing import Protocol
from dataclasses import dataclass

# 1. Модели данных (ООП для состояния)
@dataclass
class User:
    id: int
    email: str
    is_active: bool = True

# 2. Интерфейсы (Protocol из typing)
class UserRepository(Protocol):
    def get_by_id(self, id: int) -> User:
        ...
    
    def save(self, user: User) -> None:
        ...

# 3. Реализация репозитория
class PostgresUserRepository:
    def __init__(self, connection):
        self.conn = connection
    
    def get_by_id(self, id: int) -> User:
        row = self.conn.execute(
            "SELECT id, email, is_active FROM users WHERE id = ?",
            (id,)
        ).fetchone()
        return User(*row)
    
    def save(self, user: User) -> None:
        self.conn.execute(
            "UPDATE users SET email = ?, is_active = ? WHERE id = ?",
            (user.email, user.is_active, user.id)
        )

# 4. Бизнес-логика (функции, не методы)
def activate_user(user: User, repo: UserRepository) -> None:
    if user.is_active:
        raise ValueError("User already active")
    
    user.is_active = True
    repo.save(user)

# 5. FastAPI эндпоинт (использование ООП)
from fastapi import FastAPI, Depends

app = FastAPI()

def get_repo() -> UserRepository:
    return PostgresUserRepository(get_db_connection())

@app.post("/users/{user_id}/activate")
async def activate(user_id: int, repo: UserRepository = Depends(get_repo)):
    user = repo.get_by_id(user_id)
    activate_user(user, repo)
    return {"status": "activated"}

Когда я ИЗБЕГАЮ ООП

1. Трансформация данных

# ❌ Плохо: класс-обёртка
class DataProcessor:
    def normalize(self, data: list) -> list:
        return [x.strip().lower() for x in data]

processor = DataProcessor()
result = processor.normalize(["HELLO", "WORLD"])

# ✅ Хорошо: просто функция
def normalize(data: list) -> list:
    return [x.strip().lower() for x in data]

result = normalize(["HELLO", "WORLD"])

2. Утилиты и хелперы

# ❌ Плохо: статические методы в классе
class StringUtils:
    @staticmethod
    def reverse(s: str) -> str:
        return s[::-1]

# ✅ Хорошо: функции в модуле
# utils.py
def reverse(s: str) -> str:
    return s[::-1]

from utils import reverse

Реальный пример: обработка платежей

from abc import ABC, abstractmethod
from enum import Enum

class PaymentStatus(Enum):
    PENDING = "pending"
    COMPLETED = "completed"
    FAILED = "failed"

class Payment(ABC):
    def __init__(self, amount: float):
        self.amount = amount
        self.status = PaymentStatus.PENDING
    
    @abstractmethod
    def process(self) -> bool:
        pass
    
    def finalize(self, success: bool) -> None:
        self.status = (
            PaymentStatus.COMPLETED if success 
            else PaymentStatus.FAILED
        )

class StripePayment(Payment):
    def __init__(self, amount: float, token: str):
        super().__init__(amount)
        self.token = token
    
    def process(self) -> bool:
        try:
            response = stripe.charge(self.token, self.amount)
            self.finalize(response.success)
            return response.success
        except Exception as e:
            self.finalize(False)
            return False

# Использование
payment = StripePayment(100.0, "tok_visa")
success = payment.process()
print(f"Payment {payment.status.value}")

Итоговый совет

Используй ООП для:

  • Моделирования сущностей (User, Product, Order)
  • Инкапсуляции состояния (BankAccount)
  • Интерфейсов и контрактов (PaymentMethod)
  • Расширяемости через наследование

Используй функции для:

  • Трансформации данных
  • Утилит и хелперов
  • Алгоритмов
  • Один раз использующегося кода

Правило: Класс = существительное (User, Product, Account). Функция = глагол (process, calculate, validate).