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

В чем разница между SRP и SoC?

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

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

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

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

# SRP vs SoC: Разница между принципами проектирования

SRP — Single Responsibility Principle

SRP — один из 5 принципов SOLID, который гласит: класс должен иметь только одну причину для изменения.

Это означает, что каждый класс/модуль должен отвечать за одну функциональную ответственность.

Пример SRP

# ❌ Нарушение SRP — класс отвечает за множество задач
class User:
    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email
    
    def save_to_database(self):
        """Сохранение в БД"""
        db.insert('users', {'name': self.name, 'email': self.email})
    
    def send_email(self, subject: str, body: str):
        """Отправка email"""
        smtp.send(self.email, subject, body)
    
    def generate_report(self):
        """Генерация отчета"""
        return f"User Report: {self.name} - {self.email}"
    
    def validate_email(self) -> bool:
        """Валидация email"""
        return '@' in self.email

# Причины для изменения User:
# 1. Логика базы данных изменилась
# 2. SMTP конфиг изменился
# 3. Формат отчета изменился
# 4. Правила валидации email изменились

✅ Правильно — каждый класс одна ответственность

from typing import Protocol
from abc import ABC, abstractmethod

class User:
    """Отвечает только за данные пользователя"""
    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email

class UserRepository:
    """Отвечает за сохранение в БД"""
    def save(self, user: User) -> None:
        db.insert('users', {'name': user.name, 'email': user.email})
    
    def find_by_email(self, email: str) -> User:
        row = db.query('users', {'email': email})
        return User(row['name'], row['email'])

class EmailService:
    """Отвечает за отправку email"""
    def send(self, to: str, subject: str, body: str) -> None:
        smtp.send(to, subject, body)

class EmailValidator:
    """Отвечает за валидацию"""
    @staticmethod
    def is_valid(email: str) -> bool:
        return '@' in email and '.' in email.split('@')[1]

class UserReportGenerator:
    """Отвечает за генерацию отчетов"""
    @staticmethod
    def generate(user: User) -> str:
        return f"User Report: {user.name} - {user.email}"

# Использование:
user = User("John", "john@example.com")
if EmailValidator.is_valid(user.email):
    UserRepository().save(user)
    EmailService().send(user.email, "Welcome", "Hello!")
    print(UserReportGenerator.generate(user))

Причины для изменения каждого класса: только 1

  • User меняется если изменится структура пользователя
  • UserRepository меняется если меняется БД
  • EmailService меняется если меняется SMTP
  • EmailValidator меняется если меняются правила валидации
  • UserReportGenerator меняется если меняется формат отчета

SoC — Separation of Concerns

SoC — это более широкий принцип, который гласит: разделяй программу на отдельные разделы (concerns), каждый из которых решает отдельную задачу.

SoC фокусируется на архитектурном уровне, а не только на классах.

Пример SoC в архитектуре

# Слоистая архитектура (Layered Architecture)

# 🎨 PRESENTATION слой — что видит пользователь
# (Views, API endpoints, Controllers)
from fastapi import APIRouter

router = APIRouter(prefix="/api/v1/users")

@router.post("/register")
def register_user(name: str, email: str):
    # Только парсирование входа и форматирование выхода
    result = create_user_use_case.execute(name, email)
    return {"id": result.id, "email": result.email}

# ✨ APPLICATION слой — бизнес-логика
# (Use Cases, Service Layer)
class CreateUserUseCase:
    def __init__(self, repository: UserRepository, validator: EmailValidator):
        self.repository = repository
        self.validator = validator
    
    def execute(self, name: str, email: str) -> User:
        if not self.validator.is_valid(email):
            raise ValueError("Invalid email")
        
        user = User(name, email)
        self.repository.save(user)
        return user

# 📦 DOMAIN слой — бизнес-сущности
# (Entities, Domain Logic)
class User:
    def __init__(self, name: str, email: str):
        if not name or not email:
            raise ValueError("Name and email required")
        self.name = name
        self.email = email

# 🔧 INFRASTRUCTURE слой — деталі реалізації
# (Database, External APIs, Logging)
class PostgresUserRepository(UserRepository):
    def save(self, user: User):
        db.execute(
            "INSERT INTO users (name, email) VALUES (@name, @email)",
            {"name": user.name, "email": user.email}
        )

Separation of Concerns:

  • Presentation не знает о БД
  • Application не знает как отображается в UI
  • Domain не зависит от того, какая БД
  • Infrastructure легко заменяется

Ключевые различия

АспектSRPSoC
УровеньМикро (классы, методы)Макро (архитектура, слои)
ФокусОдна ответственность на классРазделение функциональности по областям
ПримерUserRepository vs EmailServicePresentation vs Business Logic vs DB
МасштабВнутри модуляМежду модулями и слоями
ЦельУпростить тестирование классаУпростить поддержку всей системы
СвязьSRP — частный случай SoCSoC — более общий принцип
# Пример: SoC включает SRP

# SoC: DOMAIN слой отделён от DB слоя
class Post:  # DOMAIN
    def __init__(self, title: str, content: str):
        self.title = title
        self.content = content
    
    def is_valid(self) -> bool:
        return len(self.title) > 0 and len(self.content) > 0

class PostRepository:  # INFRASTRUCTURE
    def save(self, post: Post):
        pass  # SQL логика здесь

# SRP внутри каждого: Post только валидирует, 
# PostRepository только сохраняет

Итого

  • SRP — убирает множественные ответственности из одного класса
  • SoC — убирает "свалку" логики и разделяет её по слоям/модулям
  • SRP более конкретный и практический
  • SoC более философский и архитектурный
  • Оба работают вместе для создания чистой и поддерживаемой кодовой базы
В чем разница между SRP и SoC? | PrepBro