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

Что такое связь владение?

2.0 Middle🔥 251 комментариев
#Soft Skills#Базы данных (NoSQL)

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

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

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

Что такое связь владения?

Связь владения (composition/aggregation) — это один из типов связей в объектно-ориентированном программировании, где один объект содержит (владеет) другие объекты. Это отношение «целое-часть», где одна сущность является компонентом другой сущности.

Типы связей

В объектно-ориентированном программировании есть несколько типов связей:

1. Association (ассоциация) — просто знание друг о друге

class Person:
    def __init__(self, name):
        self.name = name

class Car:
    def __init__(self, owner: Person):  # знает о Person
        self.owner = owner

2. Aggregation (агрегация) — слабая связь владения

Части существуют независимо от целого. Если целое удаляется, части могут продолжать существовать.

class Team:
    def __init__(self):
        self.members = []  # Список участников
    
    def add_member(self, person: Person):
        self.members.append(person)

# Даже если Team удалится, Person'ы продолжат существовать
team = Team()
person = Person("John")
team.add_member(person)
del team  # Person("John") всё ещё существует

3. Composition (композиция) — сильная связь владения

Части не существуют без целого. Если целое удаляется, части удаляются вместе с ним.

class Car:
    def __init__(self, brand):
        self.brand = brand
        self.engine = Engine()  # Car владеет Engine
        self.wheels = [Wheel() for _ in range(4)]  # Car владеет Wheel'ами

class Engine:
    def start(self):
        print("Engine started")

class Wheel:
    def rotate(self):
        print("Wheel rotating")

# Когда Car удаляется, Engine и Wheel'ы удаляются вместе
car = Car("Toyota")
del car  # Engine и Wheel'ы также удаляются

Composition vs Inheritance

Composition часто предпочитают inheritance потому что это более гибко:

Плохо — наследование (rigid):

class Vehicle:
    def start(self):
        pass

class Car(Vehicle):  # Наследуемся от Vehicle
    pass

class Bicycle(Vehicle):  # Но Bicycle не имеет двигателя
    pass

Хорошо — composition (flexible):

class Vehicle:
    def __init__(self, engine=None):  # engine опционален
        self.engine = engine
    
    def start(self):
        if self.engine:
            self.engine.start()
        else:
            print("No engine")

class Engine:
    def start(self):
        print("Engine started")

car = Vehicle(Engine())      # Car с двигателем
bicycle = Vehicle()           # Bicycle без двигателя

Практические примеры

Пример 1: Компьютер (Computer владеет компонентами)

class CPU:
    def __init__(self, cores: int):
        self.cores = cores
    
    def process(self):
        return f"Processing with {self.cores} cores"

class RAM:
    def __init__(self, size_gb: int):
        self.size = size_gb
    
    def allocate(self, amount):
        return f"Allocated {amount}GB"

class Motherboard:
    def __init__(self, model: str):
        self.model = model

class Computer:
    """Computer владеет всеми компонентами"""
    def __init__(self):
        self.cpu = CPU(cores=8)      # Composition
        self.ram = RAM(size_gb=16)   # Composition
        self.motherboard = Motherboard("ASUS Z690")  # Composition
    
    def start(self):
        print(self.cpu.process())
        print(self.ram.allocate(8))
        print(f"Running on {self.motherboard.model}")
    
    def shutdown(self):
        # Когда Computer удаляется, все компоненты удаляются
        print("Shutting down all components")

# Использование
computer = Computer()
computer.start()
# Компоненты существуют только в контексте Computer

Пример 2: Университет (имеет факультеты и студентов)

class Student:
    def __init__(self, name: str, student_id: str):
        self.name = name
        self.student_id = student_id

class Department:
    def __init__(self, name: str):
        self.name = name
        self.professors = []  # Aggregation — профессоры существуют отдельно
    
    def add_professor(self, professor):
        self.professors.append(professor)

class University:
    def __init__(self, name: str):
        self.name = name
        self.departments: list[Department] = []  # Composition — факультеты принадлежат университету
        self.students: list[Student] = []        # Aggregation — студенты могут быть в разных вузах
    
    def add_department(self, name: str) -> Department:
        dept = Department(name)
        self.departments.append(dept)
        return dept
    
    def enroll_student(self, student: Student):
        self.students.append(student)
    
    def get_info(self):
        print(f"University: {self.name}")
        print(f"Departments: {len(self.departments)}")
        print(f"Students: {len(self.students)}")

# Использование
university = University("MIT")
cs_dept = university.add_department("Computer Science")
math_dept = university.add_department("Mathematics")

john = Student("John", "S001")
university.enroll_student(john)

university.get_info()

# Если университет удалится, факультеты удаляются
# Но студенты могут остаться (они независимые сущности)

Пример 3: Ресторан (имеет меню, столы, персонал)

class Dish:
    def __init__(self, name: str, price: float):
        self.name = name
        self.price = price

class Menu:
    def __init__(self):
        self.dishes: list[Dish] = []
    
    def add_dish(self, dish: Dish):
        self.dishes.append(dish)
    
    def get_total_price(self):
        return sum(dish.price for dish in self.dishes)

class Table:
    def __init__(self, table_id: int, capacity: int):
        self.table_id = table_id
        self.capacity = capacity
        self.is_occupied = False

class Restaurant:
    def __init__(self, name: str):
        self.name = name
        self.menu = Menu()  # Composition — меню принадлежит ресторану
        self.tables: list[Table] = []  # Composition — столы принадлежат ресторану
    
    def add_table(self, capacity: int):
        table = Table(len(self.tables) + 1, capacity)
        self.tables.append(table)
    
    def add_menu_item(self, dish: Dish):
        self.menu.add_dish(dish)
    
    def get_restaurant_info(self):
        print(f"Restaurant: {self.name}")
        print(f"Menu items: {len(self.menu.dishes)}")
        print(f"Tables: {len(self.tables)}")

# Использование
restaurant = Restaurant("La Bella Italia")

# Добавляем меню
restaurant.add_menu_item(Dish("Pasta", 12.99))
restaurant.add_menu_item(Dish("Pizza", 15.99))

# Добавляем столы
for _ in range(5):
    restaurant.add_table(4)

restaurant.get_restaurant_info()

# Когда ресторан закрывается, всё удаляется

Composition с типизацией (Type Hints)

from typing import List
from dataclasses import dataclass

@dataclass
class Address:
    street: str
    city: str
    zip_code: str

@dataclass
class Person:
    name: str
    address: Address  # Composition
    friends: List['Person']  # Aggregation

# Использование
address = Address("123 Main St", "New York", "10001")
person = Person("Alice", address, [])

Правило: Предпочитай Composition Inheritance

Это правило основано на принципе:

  • Composition: более гибко и переиспользуемо
  • Inheritance: создаёт жёсткую иерархию
# Плохо — жёсткая иерархия
class Animal:
    def eat(self): pass

class Dog(Animal):
    def bark(self): pass

class RobotDog(Animal):  # Нарушает логику — робот не животное
    def charge(self): pass

# Хорошо — гибко через composition
class Eater:
    def eat(self): pass

class Barker:
    def bark(self): pass

class RealDog:
    def __init__(self):
        self.eater = Eater()
        self.barker = Barker()

class RobotDog:
    def __init__(self):
        self.barker = Barker()  # Робот лает, но не ест

Cycle Dependencies — проблема Composition

# ПРОБЛЕМА — циклическая зависимость
class Node:
    def __init__(self, value):
        self.value = value
        self.children: List['Node'] = []
        self.parent: 'Node' = None
    
    def add_child(self, child: 'Node'):
        self.children.append(child)
        child.parent = self  # Циклическая ссылка

# РЕШЕНИЕ — используй type hints
from typing import Optional

class Node:
    def __init__(self, value):
        self.value = value
        self.children: List[Node] = []
        self.parent: Optional[Node] = None

Преимущества и недостатки Composition

Преимущества:

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

Недостатки:

  • Больше кода — нужно инициализировать части
  • Делегирование — часто нужно делегировать методы
  • Циклические ссылки — сложнее управлять памятью

Заключение

Связь владения — это:

  • Отношение «целое-часть» между объектами
  • Гибкая альтернатива наследованию
  • Основа объектно-ориентированного дизайна
  • Золотое правило: предпочитай composition inheritance

Используй composition когда одна сущность содержит другую и управляет её жизненным циклом.