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

Что делает программу объектно ориентированной?

1.8 Middle🔥 141 комментариев
#Python Core

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

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

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

Что делает программу объектно ориентированной?

Объектно-ориентированная программа (ООП) — это не просто использование классов. Это идеология проектирования, основанная на нескольких ключевых принципах и концепциях.

Основные признаки ООП программы

1. Классы и объекты

# ООП: данные и методы объединены в класс
class BankAccount:
    """Класс представляет сущность с данными и поведением"""
    
    def __init__(self, owner: str, balance: float = 0):
        self.owner = owner          # Данные
        self.balance = balance
    
    def deposit(self, amount: float) -> None:
        """Метод для действия с данными"""
        self.balance += amount
    
    def withdraw(self, amount: float) -> bool:
        """Метод изменяет состояние объекта"""
        if amount <= self.balance:
            self.balance -= amount
            return True
        return False

# Использование
account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
print(f"{account.owner}: {account.balance}")  # Alice: 1300

# ❌ Не ООП: отдельные функции и данные
def deposit(balance, amount):
    return balance + amount

balance = 1000
balance = deposit(balance, 500)

Когда данные и методы объединены, это инкапсуляция — первый шаг к ООП.

2. Инкапсуляция (Encapsulation)

Сокрытие внутренних деталей, предоставление открытого интерфейса:

class User:
    """Класс с инкапсуляцией"""
    
    def __init__(self, username: str):
        self._password_hash = None  # Приватный атрибут (защищённый)
        self._email = None
        self.username = username    # Публичный атрибут
    
    @property
    def email(self):
        """Публичный интерфейс для чтения email"""
        return self._email
    
    @email.setter
    def email(self, value: str):
        """Валидация при установке email"""
        if '@' in value:
            self._email = value
        else:
            raise ValueError("Invalid email")
    
    def set_password(self, password: str) -> None:
        """Безопасное сохранение пароля (хеш, не plain text)"""
        import hashlib
        self._password_hash = hashlib.sha256(password.encode()).hexdigest()
    
    def verify_password(self, password: str) -> bool:
        """Проверка пароля"""
        import hashlib
        return self._password_hash == hashlib.sha256(password.encode()).hexdigest()

# Использование
user = User("alice")
user.email = "alice@example.com"  # Валидация автоматически
user.set_password("secret123")
print(user.verify_password("secret123"))  # True

# ❌ Плохо: доступ к приватным данным
user._password_hash  # Плохая идея!
user._email = "invalid"  # Нет валидации

Инкапсуляция — скрывает сложность, предотвращает неправильное использование.

3. Наследование (Inheritance)

Повторное использование кода через иерархию классов:

# Базовый класс
class Animal:
    """Родительский класс"""
    
    def __init__(self, name: str):
        self.name = name
    
    def speak(self) -> str:
        raise NotImplementedError("Subclasses must implement speak()")
    
    def move(self) -> str:
        return f"{self.name} is moving"

# Дочерние классы наследуют от Animal
class Dog(Animal):
    """Наследует Animal, переопределяет speak"""
    
    def speak(self) -> str:
        return f"{self.name} says: Woof!"
    
    def fetch(self, item: str) -> str:
        return f"{self.name} fetched the {item}"

class Cat(Animal):
    """Наследует Animal, переопределяет speak"""
    
    def speak(self) -> str:
        return f"{self.name} says: Meow!"

# Использование
dog = Dog("Buddy")
cat = Cat("Whiskers")

print(dog.speak())   # Buddy says: Woof!
print(cat.speak())   # Whiskers says: Meow!
print(dog.move())    # Buddy is moving (наследовано)
print(dog.fetch("ball"))  # Buddy fetched the ball (свой метод)

# Полиморфизм: одна функция работает с разными типами
def make_sound(animal: Animal) -> str:
    """Функция работает с любым Animal"""
    return animal.speak()

print(make_sound(dog))   # Buddy says: Woof!
print(make_sound(cat))   # Whiskers says: Meow!

Наследование — позволяет переиспользовать код, создавая иерархии.

4. Полиморфизм (Polymorphism)

Одинаковый интерфейс для разных типов:

# Полиморфизм: разные объекты реагируют по-разному

class Shape:
    """Абстрактный класс"""
    def area(self) -> float:
        raise NotImplementedError

class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius
    
    def area(self) -> float:
        import math
        return math.pi * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height
    
    def area(self) -> float:
        return self.width * self.height

class Triangle(Shape):
    def __init__(self, base: float, height: float):
        self.base = base
        self.height = height
    
    def area(self) -> float:
        return 0.5 * self.base * self.height

# Полиморфизм: одна функция, разное поведение
def calculate_total_area(shapes: list[Shape]) -> float:
    """Функция работает с любым Shape, вызывает подходящий area()"""
    total = 0
    for shape in shapes:
        total += shape.area()  # Полиморфизм: вызывает разные методы
    return total

# Использование
shapes = [
    Circle(5),
    Rectangle(4, 6),
    Triangle(3, 4)
]

print(calculate_total_area(shapes))  # Каждый Shape вычисляет area по-своему

Полиморфизм — разные объекты отвечают одному вызову по-разному.

5. Абстракция (Abstraction)

Сокрытие сложности, предоставление только важного:

from abc import ABC, abstractmethod

# Абстрактный класс: задаёт контракт
class Database(ABC):
    """Интерфейс для работы с БД (сокрывает детали)"""
    
    @abstractmethod
    def connect(self, host: str, port: int) -> None:
        pass
    
    @abstractmethod
    def execute_query(self, query: str):
        pass
    
    @abstractmethod
    def disconnect(self) -> None:
        pass

# Реализация для PostgreSQL
class PostgresDatabase(Database):
    def connect(self, host: str, port: int) -> None:
        print(f"Connected to PostgreSQL at {host}:{port}")
    
    def execute_query(self, query: str):
        print(f"Executing PostgreSQL query: {query}")
    
    def disconnect(self) -> None:
        print("Disconnected from PostgreSQL")

# Реализация для MongoDB
class MongoDatabase(Database):
    def connect(self, host: str, port: int) -> None:
        print(f"Connected to MongoDB at {host}:{port}")
    
    def execute_query(self, query: str):
        print(f"Executing MongoDB query: {query}")
    
    def disconnect(self) -> None:
        print("Disconnected from MongoDB")

# Использование: все равно как используется БД
def process_data(db: Database):
    """Функция не знает, какая конкретно БД, но может работать с любой"""
    db.connect("localhost", 5432)
    db.execute_query("SELECT * FROM users")
    db.disconnect()

# Лёгко менять реализацию
postgres = PostgresDatabase()
process_data(postgres)  # Работает

mongo = MongoDatabase()
process_data(mongo)     # Работает так же

Абстракция — скрывает детали реализации, показывает только контракт.

6. Принципы SOLID

Объектно-ориентированная программа следует этим принципам:

# S — Single Responsibility: каждый класс одну задачу
class UserRepository:
    """Только работа с User в БД"""
    def save(self, user): pass
    def find_by_id(self, id): pass

class UserValidator:
    """Только валидация User"""
    def validate_email(self, email): pass
    def validate_password(self, password): pass

# O — Open/Closed: открыт для расширения, закрыт для модификации
class PaymentProcessor(ABC):
    @abstractmethod
    def process(self, amount): pass

class CreditCardProcessor(PaymentProcessor):
    def process(self, amount): pass  # Добавляем новую функциональность

# L — Liskov Substitution: подклассы заменяемы базовыми
class PaymentMethod(ABC):
    @abstractmethod
    def charge(self, amount): pass

class Card(PaymentMethod):
    def charge(self, amount): pass

class Wallet(PaymentMethod):
    def charge(self, amount): pass  # Можем использовать везде, где нужен PaymentMethod

# I — Interface Segregation: не заставляй реализовать ненужные методы
# ❌ Плохо
class Worker(ABC):
    @abstractmethod
    def work(self): pass
    @abstractmethod
    def eat_lunch(self): pass  # Robot не нужно есть!

# ✅ Хорошо
class Worker(ABC):
    @abstractmethod
    def work(self): pass

class Human(Worker):
    def work(self): pass
    def eat_lunch(self): pass

class Robot(Worker):
    def work(self): pass  # Только необходимое

# D — Dependency Inversion: зависи от абстракций, не от конкретных классов
class UserService:
    def __init__(self, repository: UserRepository):  # Зависит от интерфейса
        self.repository = repository

    def get_user(self, id):
        return self.repository.find_by_id(id)

7. Чем ООП отличается от процедурного кода

# ❌ Процедурный подход (не ООП)
def create_user(name, email):
    user = {"name": name, "email": email}
    return user

def save_user(user, db):
    db.execute(f"INSERT INTO users VALUES ('{user['name']}', '{user['email']}')")

def get_user(id, db):
    return db.execute(f"SELECT * FROM users WHERE id = {id}")

# Данные и функции отделены
user = create_user("Alice", "alice@example.com")
save_user(user, db)

# ✅ ООП подход
class User:
    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email
    
    def save(self, db: UserRepository) -> None:
        db.save(self)
    
    @classmethod
    def find_by_id(cls, id: int, db: UserRepository):
        return db.find_by_id(id)

# Данные и методы вместе
user = User("Alice", "alice@example.com")
user.save(repository)

8. Критерий истинной ООП программы

Программа считается ООП, если:

criteria = {
    "✅ Группировка данных и методов в классы": "Инкапсуляция",
    "✅ Скрытие деталей реализации": "Абстракция",
    "✅ Переиспользование кода через наследование": "Наследование",
    "✅ Разные объекты по-разному реагируют на одно действие": "Полиморфизм",
    "✅ Слабая связанность между компонентами": "Декаплинг",
    "✅ Каждый класс имеет одну ответственность": "SRP",
    "✅ Модели близки к реальному миру": "Семантика"
}

# Просто использование классов — не ООП!
class Config:  # Только хранилище данных
    def __init__(self):
        self.db_host = "localhost"
        self.db_port = 5432

config = Config()  # ❌ Это просто data holder, не ООП

# ООП:
class DatabaseConnection:  # Моделирует реальный объект
    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port
        self._connection = None
    
    def connect(self) -> None:
        # Скрытие сложности подключения
        pass
    
    def close(self) -> None:
        pass

Итоговое резюме

Объектно-ориентированная программа:

  1. Объединяет данные и методы в классы
  2. Скрывает внутренние детали (инкапсуляция)
  3. Переиспользует код через наследование
  4. Обрабатывает разные типы единообразно (полиморфизм)
  5. Моделирует реальный мир через классы
  6. Слабо связана (компоненты независимы)
  7. Следует SOLID принципам

Не ООП, если:

  • ❌ Классы только как контейнеры данных
  • ❌ Функции вне классов обрабатывают объекты
  • ❌ Глубокие циклы наследования (отсутствует полиморфизм)
  • ❌ Тесная связанность (изменение одного класса ломает другие)
  • ❌ Классы делают слишком много (нарушение SRP)

Истинная ООП — это философия проектирования, а не просто использование синтаксиса Python.

Что делает программу объектно ориентированной? | PrepBro