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

Какие знаешь композиции?

2.0 Middle🔥 131 комментариев
#SOLID и паттерны проектирования#ООП

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

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

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

Композиции в Java

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

Основной принцип

"Composition over inheritance" — компоновка предпочтительнее наследования. Вместо создания иерархии классов через наследование, мы создаём классы из других объектов.

// ❌ Плохо: наследование
class Car extends Vehicle {
    // Car IS-A Vehicle
}

// ✅ Хорошо: композиция
class Car {
    private Engine engine;      // Car HAS-A Engine
    private Transmission transmission;  // Car HAS-A Transmission
    private Wheels wheels;      // Car HAS-A Wheels
}

1. Простая композиция

Один объект содержит другой объект.

// Класс Engine
public class Engine {
    private int horsePower;
    private String fuelType;
    
    public Engine(int horsePower, String fuelType) {
        this.horsePower = horsePower;
        this.fuelType = fuelType;
    }
    
    public void start() {
        System.out.println("Engine started: " + horsePower + " HP");
    }
    
    public void stop() {
        System.out.println("Engine stopped");
    }
}

// Класс Car содержит Engine
public class Car {
    private String brand;
    private Engine engine;  // Композиция
    
    public Car(String brand, Engine engine) {
        this.brand = brand;
        this.engine = engine;
    }
    
    public void startCar() {
        engine.start();  // Использование объекта Engine
        System.out.println(brand + " is ready to go");
    }
    
    public void stopCar() {
        engine.stop();
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        Engine engine = new Engine(150, "Petrol");
        Car car = new Car("Toyota", engine);
        car.startCar();  // Engine started: 150 HP
    }
}

2. Множественная композиция

Один объект содержит несколько других объектов.

public class Wheel {
    private int size;  // в дюймах
    
    public Wheel(int size) {
        this.size = size;
    }
    
    public void rotate() {
        System.out.println("Wheel (" + size + " inches) rotating");
    }
}

public class Transmission {
    private String type;  // Manual, Automatic, CVT
    
    public Transmission(String type) {
        this.type = type;
    }
    
    public void shift(int gear) {
        System.out.println("Shifting to gear " + gear + " (" + type + ")");
    }
}

public class Car {
    private String brand;
    private Engine engine;              // Композиция 1
    private Transmission transmission;  // Композиция 2
    private Wheel[] wheels;             // Композиция 3 (массив)
    
    public Car(String brand, Engine engine, Transmission transmission) {
        this.brand = brand;
        this.engine = engine;
        this.transmission = transmission;
        this.wheels = new Wheel[4];  // 4 колеса
        for (int i = 0; i < 4; i++) {
            wheels[i] = new Wheel(18);
        }
    }
    
    public void drive() {
        engine.start();
        transmission.shift(1);
        for (Wheel wheel : wheels) {
            wheel.rotate();
        }
    }
}

3. Вложенная композиция

Одна композиция содержит другую.

public class Address {
    private String street;
    private String city;
    private String zipCode;
    
    public Address(String street, String city, String zipCode) {
        this.street = street;
        this.city = city;
        this.zipCode = zipCode;
    }
    
    @Override
    public String toString() {
        return street + ", " + city + " " + zipCode;
    }
}

public class Person {
    private String name;
    private Address address;  // Композиция
    
    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
    
    public void printInfo() {
        System.out.println("Name: " + name);
        System.out.println("Address: " + address);
    }
}

public class Company {
    private String companyName;
    private List<Person> employees;  // Композиция списков Person
    private Address headquarters;    // Композиция Address
    
    public Company(String companyName, Address headquarters) {
        this.companyName = companyName;
        this.headquarters = headquarters;
        this.employees = new ArrayList<>();
    }
    
    public void addEmployee(Person employee) {
        employees.add(employee);
    }
    
    public void printCompanyInfo() {
        System.out.println("Company: " + companyName);
        System.out.println("Headquarters: " + headquarters);
        System.out.println("Employees: " + employees.size());
    }
}

4. Композиция с коллекциями

Один объект содержит коллекцию других объектов.

public class Student {
    private String name;
    private String id;
    
    public Student(String name, String id) {
        this.name = name;
        this.id = id;
    }
    
    @Override
    public String toString() {
        return name + " (" + id + ")";
    }
}

public class Course {
    private String courseName;
    private String courseId;
    private List<Student> students;  // Композиция коллекции
    private int maxCapacity;
    
    public Course(String courseName, String courseId, int maxCapacity) {
        this.courseName = courseName;
        this.courseId = courseId;
        this.maxCapacity = maxCapacity;
        this.students = new ArrayList<>();
    }
    
    public boolean enrollStudent(Student student) {
        if (students.size() < maxCapacity) {
            students.add(student);
            return true;
        }
        return false;
    }
    
    public void printStudents() {
        System.out.println("Course: " + courseName);
        System.out.println("Students enrolled: " + students.size());
        for (Student s : students) {
            System.out.println("  " + s);
        }
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        Course javaCourse = new Course("Java Programming", "CS101", 30);
        
        javaCourse.enrollStudent(new Student("Alice", "001"));
        javaCourse.enrollStudent(new Student("Bob", "002"));
        javaCourse.enrollStudent(new Student("Charlie", "003"));
        
        javaCourse.printStudents();
    }
}

5. Агрегация (слабая композиция)

Отличие от композиции: объекты могут существовать независимо.

public class Department {
    private String name;
    private List<Employee> employees;  // Агрегация: Employee может существовать без Department
    
    public Department(String name) {
        this.name = name;
        this.employees = new ArrayList<>();
    }
    
    public void assignEmployee(Employee employee) {
        employees.add(employee);
    }
    
    public void removeEmployee(Employee employee) {
        employees.remove(employee);
        // Employee продолжит существовать
    }
}

public class Employee {
    private String name;
    private String employeeId;
    
    public Employee(String name, String employeeId) {
        this.name = name;
        this.employeeId = employeeId;
    }
}

// Агрегация: Employee существует независимо от Department
Employee emp = new Employee("John", "EMP001");
Department dept = new Department("Engineering");
dept.assignEmployee(emp);
dept.removeEmployee(emp);  // emp ещё существует

6. Разница между композицией и наследованием

// Наследование: IS-A отношение
class Dog extends Animal {
    // Dog IS-A Animal
    // Жёсткая иерархия
    // Наследует всё из Animal
}

// Композиция: HAS-A отношение
class Dog {
    private Head head;      // HAS-A Head
    private Tail tail;      // HAS-A Tail
    private List<Leg> legs; // HAS-A Legs
    private Bark bark;      // HAS-A Bark behavior
    
    public void sound() {
        bark.bark();  // делегирование
    }
}

// Композиция лучше при условиях:
// 1. Несколько независимых поведений
// 2. Динамическое изменение поведения
// 3. Избежать хрупкой базовой иерархии

7. Делегирование в композиции

public class Logger {
    public void log(String message) {
        System.out.println("[LOG] " + message);
    }
}

public class UserService {
    private Logger logger;  // Композиция
    
    public UserService(Logger logger) {
        this.logger = logger;
    }
    
    public void createUser(String name) {
        logger.log("Creating user: " + name);
        // ... логика создания
        logger.log("User created successfully");
    }
    
    public void deleteUser(String id) {
        logger.log("Deleting user with ID: " + id);
        // ... логика удаления
        logger.log("User deleted successfully");
    }
}

// Использование
UserService service = new UserService(new Logger());
service.createUser("Alice");

8. Композиция и полиморфизм

public interface Payment {
    void process(BigDecimal amount);
}

public class CreditCardPayment implements Payment {
    @Override
    public void process(BigDecimal amount) {
        System.out.println("Processing credit card payment: " + amount);
    }
}

public class PayPalPayment implements Payment {
    @Override
    public void process(BigDecimal amount) {
        System.out.println("Processing PayPal payment: " + amount);
    }
}

public class ShoppingCart {
    private List<Item> items;
    private Payment paymentMethod;  // Полиморфная композиция
    
    public ShoppingCart(Payment paymentMethod) {
        this.paymentMethod = paymentMethod;
        this.items = new ArrayList<>();
    }
    
    public void checkout() {
        BigDecimal total = items.stream()
            .map(Item::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        paymentMethod.process(total);  // Вызовется нужная реализация
    }
    
    public void setPaymentMethod(Payment newMethod) {
        this.paymentMethod = newMethod;  // Динамическая смена поведения
    }
}

// Использование
ShoppingCart cart = new ShoppingCart(new CreditCardPayment());
// ...
cart.setPaymentMethod(new PayPalPayment());  // Смена способа оплаты
cart.checkout();

Плюсы и минусы композиции

Плюсы:

  • Гибкость: легко менять поведение во время выполнения
  • Слабая связанность: компоненты независимы
  • Переиспользование: один класс может содержать разные объекты
  • Избежание хрупкой иерархии наследования
  • Принцип единственной ответственности (SRP)

Минусы:

  • Больше кода для создания объектов
  • Нужно самостоятельно реализовать методы делегирования
  • Иногда менее интуитивна чем наследование

Итого

Композиция — это мощный инструмент для создания гибких и переиспользуемых компонентов. Она позволяет:

  1. Избежать тесной связанности с иерархией наследования
  2. Динамически менять поведение во время выполнения
  3. Переиспользовать код из разных компонентов
  4. Упростить тестирование через dependency injection
  5. Соблюдать SOLID принципы, особенно Single Responsibility и Dependency Inversion

Основное правило: предпочитай композицию наследованию, если нет явной иерархии IS-A отношений.

Какие знаешь композиции? | PrepBro