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

Какие знаешь виды отношений в диаграмме классов?

2.3 Middle🔥 171 комментариев
#Нотации и диаграммы

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

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

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

Виды отношений в диаграмме классов (UML Class Diagram)

Диаграмма классов — это фундаментальный инструмент объектно-ориентированного проектирования. Отношения между классами показывают как они взаимодействуют и зависят друг от друга. Есть 6 основных типов отношений.

1. Наследование (Inheritance / Generalization)

Суть: класс-потомок наследует свойства и методы класса-родителя. Отношение IS-A ("является").

Обозначение: стрелка с незаполненным треугольником, направленная к родителю.

Диаграмма:

     ┌─────────────┐
     │   Animal    │
     │─────────────│
     │ + name      │
     │ + eat()     │
     └─────────────┘
            ▲
            │ наследует
            │ (незаполненная стрелка)
     ┌──────┴──────┐
     │             │
┌────────┐   ┌────────┐
│  Dog   │   │  Cat   │
├────────┤   ├────────┤
│ + bark()│  │ + meow()│
└────────┘   └────────┘

Код:

class Animal:
    def eat(self): pass

class Dog(Animal):  # наследуется
    def bark(self): pass

Характеристики:

  • Потомок имеет все методы и свойства родителя
  • Потомок может переопределить (override) методы родителя
  • Сильная связанность между классами

2. Реализация (Implementation)

Суть: класс реализует (implements) интерфейс. Обязательно должен иметь все методы интерфейса.

Обозначение: пунктирная стрелка с незаполненным треугольником, направленная к интерфейсу.

Диаграмма:

       ┌─────────────┐
       │ <<interface>>│
       │   Animal    │
       ├─────────────┤
       │ + eat()     │
       │ + sleep()   │
       └─────────────┘
            ▲
            │ реализует
            │ (пунктирная стрелка)
     ┌──────┴──────┐
     │             │
┌────────┐   ┌────────┐
│  Dog   │   │  Cat   │
├────────┤   ├────────┤
│ + eat()│   │ + eat() │
│ + sleep()   │ + sleep()
└────────┘   └────────┘

Код:

class Animal(Interface):
    def eat(self): pass
    def sleep(self): pass

class Dog(Animal):  # реализует
    def eat(self): print("Dog eats")
    def sleep(self): print("Dog sleeps")

Характеристики:

  • Контракт: класс ДОЛЖЕН реализовать все методы интерфейса
  • Слабая связанность (если используется правильно)
  • Позволяет polymorphism

3. Ассоциация (Association)

Суть: отношение между двумя классами, они знают друг о друге. Один класс "использует" другой.

Обозначение: простая линия со стрелкой или без неё.

Пример: Студент посещает Курс

┌──────────┐        ┌────────┐
│ Student  │────────│ Course │
├──────────┤  1   * ├────────┤
│ - id     │        │ - name │
│ - name   │        │ - code │
└──────────┘        └────────┘

Один студент может посещать много курсов.

Код:

class Student:
    def __init__(self, name):
        self.name = name
        self.courses = []  # ассоциация
    
    def enroll(self, course):
        self.courses.append(course)

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

Мощность (Cardinality) — цифры на линии:

  • 1..1 — один к одному
  • 1.. или 1..n* — один ко многим
  • .. или m..n — много ко многим
  • 0..1 — ноль или один (опциональная)

Виды ассоциаций:

  • Unidirectional — только одна сторона знает о другой
  • Bidirectional — обе стороны знают друг о друге

4. Агрегация (Aggregation)

Суть: отношение целое-часть, где часть может существовать отдельно от целого. Слабое целое.

Обозначение: линия с незаполненным ромбом со стороны целого.

Пример: Команда и Игроки

    ┌───────────┐         ┌────────┐
    │   Team    │◇────────│ Player │
    ├───────────┤ 1     * ├────────┤
    │ - name    │         │ - name │
    │ - players │         │ - number
    └───────────┘         └────────┘

Команда состоит из игроков, но игрок может существовать без команды (может быть свободным агентом).

Код:

class Team:
    def __init__(self, name):
        self.name = name
        self.players = []  # агрегация
    
    def add_player(self, player):
        self.players.append(player)

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

Характеристики:

  • Часть может жить отдельно
  • Слабое связывание
  • Часто реализуется через коллекции (list, set)

5. Композиция (Composition)

Суть: отношение целое-часть, где часть НЕ может существовать без целого. Сильное целое. Has-a сильное.

Обозначение: линия с заполненным (чёрным) ромбом со стороны целого.

Пример: Автомобиль и Двигатель

┌─────────────┐        ┌────────┐
│  Car        │■────────│ Engine │
├─────────────┤ 1     1 ├────────┤
│ - model     │         │ - power│
│ - engine    │         └────────┘
└─────────────┘

Автомобиль БЕЗ двигателя это не машина. Двигатель принадлежит ТОЛЬКО этому автомобилю.

Код:

class Engine:
    def __init__(self, power):
        self.power = power

class Car:
    def __init__(self, model, power):
        self.model = model
        self.engine = Engine(power)  # композиция
    
    def __del__(self):
        # Когда машина удаляется, двигатель тоже удаляется
        del self.engine

Характеристики:

  • Часть НЕ может существовать без целого
  • Сильное связывание
  • При удалении целого удаляется часть (в коде или БД)
  • Отношение owner-owned

6. Зависимость (Dependency)

Суть: один класс использует другой временно, обычно как параметр метода или локальную переменную. Самая слабая связь.

Обозначение: пунктирная стрелка.

Пример: Водитель пользуется Автомобилем

┌────────────┐  uses  ┌────────┐
│  Driver    │ ......→│  Car   │
├────────────┤        ├────────┤
│ - name     │        │ - model│
│ + drive(car)        └────────┘
└────────────┘

Код:

class Driver:
    def __init__(self, name):
        self.name = name
    
    def drive(self, car):  # зависимость от Car
        return f"{self.name} drives {car.model}"

class Car:
    def __init__(self, model):
        self.model = model

Характеристики:

  • Использование классов в параметрах методов
  • Использование как локальных переменных
  • Самая слабая связанность
  • Исчезает когда метод заканчивается

Сравнительная таблица

ОтношениеОбозначениеСвязанностьЖизненный циклПример
Наследование▲ (сплошная)Очень сильнаяСтатическаяDog extends Animal
Реализация▲ (пунктир)СильнаяСтатическаяDog implements Animal
Ассоциация─ →СредняяДинамическаяStudent has Course
Агрегация◇─СлабаяДинамическаяTeam has Players
Композиция■─СильнаяСтатическаяCar has Engine
Зависимость··→Очень слабаяВременнаяDriver uses Car

Практический пример: Образовательная система

                  ┌─────────────────┐
                  │  <<interface>>  │
                  │    IPerson      │
                  ├─────────────────┤
                  │ + getName()     │
                  │ + getEmail()    │
                  └─────────────────┘
                         ▲
                         │ реализует
           ┌─────────────┴──────────────┐
           │                            │
      ┌─────────┐              ┌──────────┐
      │ Student │              │ Professor│
      ├─────────┤              ├──────────┤
      │ - id    │              │ - id     │
      │ - gpa   │              │ - rank   │
      └─────────┘              └──────────┘
           │                         │
           │ ассоциация             │
           │ (много ко многим)      │ ассоциация
           │                    ┌────────┐
           └─────────────────■──┤ Course │
          агрегация 1      * ├────────┤
                           │ - name  │
                           │ - credits
                           └────────┘
                                │
                                │ композиция
                                │
                          ┌─────────────┐
                          │ Assignment  │
                          ├─────────────┤
                          │ - name      │
                          │ - dueDate   │
                          └─────────────┘

Best Practices

Используй наследование когда есть IS-A отношение ✅ Предпочитай Composition вместо Inheritance (принцип Composition over Inheritance) ✅ Используй интерфейсы для определения контрактов ✅ Минимизируй связанность — выбирай слабые отношения где возможно ✅ Явно обозначь мощность — чтобы было ясно сколько объектов может быть ✅ Не переусложняй — не все связи нужно отражать в диаграмме

Принцип DIP (Dependency Inversion Principle)

Плохо:

class Driver:
    def drive(self, car):
        if isinstance(car, Car):
            car.drive()

Зависит от конкретного класса Car.

Хорошо:

class Driver:
    def drive(self, vehicle):  # зависит от интерфейса
        vehicle.drive()

Теперь может работать с любым Vehicle.

Вывод

Отношения в диаграмме классов показывают архитектуру системы:

  • Наследование и Реализация — для иерархий и полиморфизма
  • Ассоциация — для использования
  • Агрегация и Композиция — для структур (целое-часть)
  • Зависимость — для временных использований

Правильно спроектированные отношения делают код модульным, переиспользуемым и легко тестируемым.

Какие знаешь виды отношений в диаграмме классов? | PrepBro