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

Где лучше применять ассоциацию?

2.0 Middle🔥 171 комментариев
#Основы Java

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

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

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

# Где применять ассоциацию (Association) в Java

Ассоциация — это отношение между классами, которое указывает, что один класс использует или работает с другим классом. Это один из четырёх типов отношений в UML (наряду с агрегацией, композицией и наследованием).

Типы ассоциаций

1. Простая ассоциация (Association)

Один класс ссылается на другой, но не владеет его жизненным циклом:

public class Driver {
    private Car car;  // Ассоциация: водитель может водить разные машины
    
    public void setCar(Car car) {
        this.car = car;
    }
    
    public void drive() {
        System.out.println("Driving " + car.getModel());
    }
}

public class Car {
    private String model;
    
    public Car(String model) {
        this.model = model;
    }
    
    public String getModel() {
        return model;
    }
}

// Использование
Driver driver = new Driver();
Car car1 = new Car("BMW");
Car car2 = new Car("Mercedes");

driver.setCar(car1);
driver.drive();  // Driving BMW

driver.setCar(car2);  // Водитель может менять машину
driver.drive();  // Driving Mercedes

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

Один класс содержит другой, но не владеет его жизненным циклом (has-a):

public class Library {
    private List<Book> books;  // Агрегация
    
    public Library() {
        this.books = new ArrayList<>();
    }
    
    public void addBook(Book book) {
        books.add(book);
    }
    
    public void removeBook(Book book) {
        books.remove(book);
    }
}

public class Book {
    private String title;
    
    public Book(String title) {
        this.title = title;
    }
}

// Использование
Book book1 = new Book("Clean Code");
Book book2 = new Book("Design Patterns");

Library library = new Library();
library.addBook(book1);
library.addBook(book2);

// Если библиотека закроется, книги остаются (не их жизненный цикл)

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

Один класс владеет другим (strong ownership), жизненные циклы связаны:

public class House {
    private List<Room> rooms;  // Композиция: комнаты принадлежат дому
    
    public House() {
        this.rooms = new ArrayList<>();
        // Создаём комнаты как часть дома
        rooms.add(new Room("Kitchen"));
        rooms.add(new Room("Bedroom"));
    }
    
    public void demolish() {
        // Удаляя дом, удаляем и комнаты
        rooms.clear();
    }
}

public class Room {
    private String name;
    
    public Room(String name) {
        this.name = name;
    }
}

// Использование
House house = new House();  // Создаём дом -> комнаты автоматически создаются
house.demolish();  // Разрушаем дом -> комнаты исчезают

Где применять каждую ассоциацию

Применять простую ассоциацию

Когда:

  • Классы не зависят друг от друга в плане жизненного цикла
  • Один объект может взаимодействовать с несколькими другими
  • Отношение слабое и может быть изменено в runtime

Примеры:

// Student может быть в разных Group
public class Student {
    private String name;
    private Group currentGroup;  // Ассоциация
    
    public void switchGroup(Group newGroup) {
        this.currentGroup = newGroup;  // Легко менять
    }
}

public class Group {
    private String name;
}

// Manager может управлять разными Team
public class Manager {
    private List<Team> teams;  // Ассоциация
    
    public void addTeam(Team team) {
        teams.add(team);
    }
}

public class Team {
    private String name;
}

// User может иметь разные Permission
public class User {
    private String email;
    private List<Permission> permissions;  // Ассоциация
    
    public void grantPermission(Permission perm) {
        permissions.add(perm);
    }
}

public class Permission {
    private String name;
}

Применять агрегацию

Когда:

  • Один класс содержит коллекцию других объектов
  • Содержащий класс не отвечает за создание/удаление элементов
  • Элементы могут существовать независимо
  • Отношение типа "has-a"

Примеры:

// Project содержит Task, но Task может существовать отдельно
public class Project {
    private String name;
    private List<Task> tasks;  // Агрегация
    
    public void addTask(Task task) {
        tasks.add(task);
    }
}

public class Task {
    private String description;
    private boolean completed;
}

// Playlist содержит Song
public class Playlist {
    private String name;
    private List<Song> songs;  // Агрегация
    
    public void addSong(Song song) {
        songs.add(song);
    }
}

public class Song {
    private String title;
    private String artist;
}

// Department содержит Employee
public class Department {
    private String name;
    private List<Employee> employees;  // Агрегация
    
    public void hireEmployee(Employee emp) {
        employees.add(emp);
    }
    
    public void fireEmployee(Employee emp) {
        employees.remove(emp);
        // Employee может найти другую работу
    }
}

public class Employee {
    private String name;
    private double salary;
}

Применять композицию

Когда:

  • Один класс владеет частями другого класса
  • Части не могут существовать без целого
  • Жизненные циклы тесно связаны
  • "has-a" с сильной связью

Примеры:

// Car содержит Engine (не может быть без Engine)
public class Car {
    private Engine engine;  // Композиция
    private Wheel[] wheels;  // Композиция
    
    public Car() {
        this.engine = new Engine();  // Создаём части в конструкторе
        this.wheels = new Wheel[4];
        for (int i = 0; i < 4; i++) {
            wheels[i] = new Wheel();
        }
    }
    
    public void start() {
        engine.start();  // Используем части
    }
}

public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

public class Wheel {
    private int diameter = 16;
}

// Computer содержит RAM, HDD (не могут быть отдельно)
public class Computer {
    private RAM ram;  // Композиция
    private HDD hdd;  // Композиция
    private CPU cpu;  // Композиция
    
    public Computer(int ramSize, int hddSize) {
        this.ram = new RAM(ramSize);
        this.hdd = new HDD(hddSize);
        this.cpu = new CPU();
    }
    
    public void shutdown() {
        // Компьютер и его компоненты удаляются вместе
    }
}

// Order содержит OrderItem (товары в заказе)
public class Order {
    private Long id;
    private List<OrderItem> items;  // Композиция
    
    public Order() {
        this.items = new ArrayList<>();
    }
    
    public void addItem(String productName, int quantity) {
        items.add(new OrderItem(productName, quantity));
    }
    
    public double getTotalPrice() {
        return items.stream()
            .mapToDouble(OrderItem::getPrice)
            .sum();
    }
}

public class OrderItem {
    private String productName;
    private int quantity;
    private double price;
}

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

ХарактеристикаАссоциацияАгрегацияКомпозиция
Сила связиСлабаяСредняяСильная
ВладениеНетСлабоеПолное
Жизненные циклыНезависимыеНезависимыеСвязанные
НаправлениеМожет быть двустороннейОдносторонняяОдносторонняя
Когда создаётсяМожет быть любое времяМожет быть любое времяПри создании владельца
Когда удаляетсяНезависимоНезависимоВместе с владельцем
ПримерыDriver-Car, Teacher-StudentLibrary-Book, Team-MemberCar-Engine, House-Room

Best Practices

1. Prefer Composition over Inheritance

// Плохо: наследование
public class PoweredCar extends Car {
    // Наследуем всё, даже не нужное
}

// Хорошо: композиция
public class Car {
    private Engine engine;  // Композиция
    private List<Wheel> wheels;  // Композиция
    
    public void start() {
        engine.start();
    }
}

2. Используй setter для ассоциации, constructor для композиции

// Ассоциация: setter
public class Manager {
    private Team team;
    
    public void setTeam(Team team) {  // Может менять
        this.team = team;
    }
}

// Композиция: constructor
public class Car {
    private final Engine engine;  // final — не может менять
    
    public Car() {
        this.engine = new Engine();  // Создаём в constructor
    }
}

3. Избегай циклических ассоциаций

// Плохо: циклическая ассоциация
public class Parent {
    private List<Child> children;
    // Если добавить: private Parent parent; — циклическая ссылка
}

// Хорошо: только в одну сторону
public class Parent {
    private List<Child> children;
}

public class Child {
    // Ссылка на родителя, если нужна
    private Parent parent;  // Но обычно используют ID
}

Практический пример: E-commerce System

// Композиция: Order содержит OrderItems
public class Order {
    private Long id;
    private LocalDateTime createdAt;
    private List<OrderItem> items;  // Композиция
    
    public Order() {
        this.items = new ArrayList<>();
    }
}

public class OrderItem {
    private Long id;
    private Product product;
    private int quantity;
}

// Агрегация: Warehouse содержит Stock
public class Warehouse {
    private String location;
    private List<Stock> stocks;  // Агрегация
    
    public void addStock(Stock stock) {
        stocks.add(stock);
    }
}

public class Stock {
    private Product product;
    private int quantity;
}

// Ассоциация: Seller может продавать разные Product
public class Seller {
    private String name;
    private List<Product> products;  // Ассоциация
    
    public void listProduct(Product product) {
        products.add(product);
    }
}

public class Product {
    private Long id;
    private String name;
    private double price;
}

Выводы

  1. Ассоциация — для слабых, изменяемых отношений
  2. Агрегация — для контейнеров со слабой связью
  3. Композиция — для сильных, неразлучных отношений
  4. Правило: Prefer composition over inheritance
  5. На практике: Большинство случаев — это просто ассоциация или композиция

Правильное использование ассоциаций делает код гибким, тестируемым и поддерживаемым.

Где лучше применять ассоциацию? | PrepBro