Какие знаешь композиции?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Композиции в 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)
Минусы:
- Больше кода для создания объектов
- Нужно самостоятельно реализовать методы делегирования
- Иногда менее интуитивна чем наследование
Итого
Композиция — это мощный инструмент для создания гибких и переиспользуемых компонентов. Она позволяет:
- Избежать тесной связанности с иерархией наследования
- Динамически менять поведение во время выполнения
- Переиспользовать код из разных компонентов
- Упростить тестирование через dependency injection
- Соблюдать SOLID принципы, особенно Single Responsibility и Dependency Inversion
Основное правило: предпочитай композицию наследованию, если нет явной иерархии IS-A отношений.