Что такое связь владение?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое связь владения?
Связь владения (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 когда одна сущность содержит другую и управляет её жизненным циклом.