Лабораторно упражнение 1
Композиция и агрегация
В обектно-ориентираното програмиране композицията и агрегацията представляват форми на асоциация между обекти, чрез които се моделират връзки от тип “част - цяло”. Те се използват за изграждане на по-сложни системи чрез комбиниране на по-малки обекти.
Основното предназначение на композицията и агрегацията е:
-
моделиране на реални зависимости между обектите
-
реализиране на модулност и разделяне на отговорностите
-
увеличаване на възможността за преизползване на съществуващ код
-
намаляване зависимостите между обектите (в сравнение с използването на наследяване).
Композиция
Композицията се използва за създаване на силна връзка между обектите и е подходяща в случаите, когато единия обект (“частта”) не може да съществува смислено без другия (“цялото”). При унищожаване на композитни обект, частта също се унищожава.
Съществуват различни типове композиция според начина на тяхната реализация:
-
класическа композиция, при която “частите” се създават в самия композитен обект
public class Address {
private String town;
private String streetName;
private int number;
// constructor, getters, setters, methods
}
public class Building {
private Address address;
public Building() {
this.address = new Address(); // обектът се създава вътре
}
// methods
}
- композиция с множественост (1-to-many)
public class DocumentationChapter {
private int number;
private String chapterTitle;
public DocumentationChapter(int number, String chapterTitle) {
this.number = number;
this.chapterTitle = chapterTitle;
}
}
public class ProjectDocumentation {
private List<DocumentationChapter> documentation = new ArrayList<>();
public ProjectDocumentation() {
documentation.add(new DocumentationChapter(1, "Title page"));
documentation.add(new DocumentationChapter(2, "Project introduction"));
documentation.add(new DocumentationChapter(3, "Project body"));
documentation.add(new DocumentationChapter(4, "Conclusion"));
}
}
- композиция с контрол върху достъпа, при която частите са капсулирани и композитния обект не позволява външни модификации
public class DocumentationChapter {
private int number;
private String chapterTitle;
public DocumentationChapter(int number, String chapterTitle) {
this.number = number;
this.chapterTitle = chapterTitle;
}
}
public class ProjectDocumentation {
private List<DocumentationChapter> documentation;
public ProjectDocumentation() {
documentation = List.of(
new DocumentationChapter(1, "Title page"),
new DocumentationChapter(2, "Project introduction"),
new DocumentationChapter(3, "Project body"),
new DocumentationChapter(4, "Conclusion")
);
}
public List<DocumentationChapter> getDocumentation() {
return Collections.unmodifiableList(documentation);
}
}
Агрегация
Агрегацията се използва в случаи, в които връзката между обектите е по-слаба и частта може да съществува самостоятелно извън контекста на сложния обект. Обект, който е използван като част от сложен такъв, може да бъде споделян между различни сложни обекти и в различни контексти, като по този начин той е независим от тяхното съществуване.
Аналогично на композицията, според различната реализация съществуват няколко типа агрегация:
- проста агрегация, при която частите се подават отвън и съществуват независимо от сложния обект
public class Book {
// properties, constructors, methods
}
public class Library {
private List<Book> catalog;
public Library(List<Book> catalog) {
this.catalog = catalog; // подадени отвън
}
}
- еднопосочна агрегация, при която само единия обект знае за другия
public class Book {
private String title;
public Book(String title) {
this.title = title;
}
// methods
}
public class Reader {
private String name;
private Set<Book> books = new HashSet<>();
public Reader(String name) {
this.name = name;
}
public void borrowBook(Book book) {
books.add(book);
}
}
- двупосочна агрегация, при която и двете страни знаят една за друга
public class Book {
private String title;
private Set<Reader> readers = new HashSet<>();
public Book(String title) {
this.title = title;
}
public void addReader(Reader reader) {
readers.add(reader);
}
}
public class Reader {
private String name;
private Set<Book> books = new HashSet<>();
public Reader(String name) {
this.name = name;
}
public void borrowBook(Book book) {
books.add(book);
book.addReader(this);
}
}
- множествена агрегация чрез посредник (в случая, библиотека), при която обектите остават независими един от друг и се подават отвън
public class Book {
private String title;
public Book(String title) {
this.title = title;
}
}
public class Reader {
private String name;
public Reader(String name) {
this.name = name;
}
}
public class Library {
private Set<Book> books = new HashSet<>();
private Set<Reader> readers = new HashSet<>();
public void addBook(Book book) {
books.add(book);
}
public void addReader(Reader reader) {
readers.add(reader);
}
}
- агрегация чрез асоциативен клас (асоциативна агрегация), при която отново обектите са независими
public class Book {
private String title;
public Book(String title) {
this.title = title;
}
}
public class Reader {
private String name;
public Reader(String name) {
this.name = name;
}
}
public class LoanBook {
private Book book;
private Reader reader;
private LocalDate date;
public LoanBook(Book book, Reader reader, LocalDate date) {
this.book = book;
this.reader = reader;
this.date = date;
}
}
Композиция vs Агрегация
Композицията моделира структурна зависимост, докато агрегацията моделира логическа принадлежност.
Таблицата по-долу обобщава и сравнява двете форми по няколко критерия.
| Критерий | Композиция | Агрегация |
|---|---|---|
| степен на свързаност | силна | слаба |
| жизнен цикъл | зависим | независим |
| собственост | има | няма |
| споделяемост | не | да |
Изборът между асоциация, агрегация и композиция е архитектурно решение, което определя степента на зависимост, гъвкавост и поддържаемост на системата.
В практиката композицията и агрегацията се използват за:
-
структуриране на домейн модели
-
изграждане на микросървисни и слоести архитектури
-
dependency injection
-
създаване на гъвкави и заменяеми компоненти.
Тяхното използване подпомага прилагането на принципи като Single Responsibility Principle, Dependency Inversion Principle и Low Coupling / High Cohesion.