Aggregation

Aggregation is a design principle where a class contains references to objects of other classes, representing a “has-a” relationship. Unlike composition, aggregation implies a weaker relationship between the container and its components, meaning the contained objects can exist independently of the container.

Key Characteristics

  1. Weak Ownership: The lifetime of the contained objects is not tied to the lifetime of the container.
  2. Independent Objects: Contained objects can be shared among multiple containers.
  3. Loose Coupling: Promotes loose coupling between classes, making code more modular and maintainable.

Example of Aggregation

Consider a Library class and a Book class where the library contains books, but the books can exist independently.

#include <iostream>
#include <vector>
#include <string>

class Book {
public:
    std::string title;

    Book(const std::string& t) : title(t) {}

    void display() const {
        std::cout << "Book: " << title << std::endl;
    }
};

class Library {
private:
    std::vector<Book*> books;  // Aggregation

public:
    void addBook(Book* book) {
        books.push_back(book);
    }

    void showBooks() const {
        for (const auto& book : books) {
            book->display();
        }
    }
};

int main() {
    Book book1("1984");
    Book book2("Brave New World");

    Library library;
    library.addBook(&book1);
    library.addBook(&book2);

    library.showBooks();

    return 0;
}

Characteristics of Aggregation

  1. Shared References: Multiple classes can reference the same object.
  2. Independent Lifecycle: The existence of the contained objects does not depend on the container.
  3. Non-exclusive Ownership: Objects are not exclusively owned by the container.

Differences Between Aggregation and Composition

AspectAggregationComposition
OwnershipWeak ownershipStrong ownership
LifecycleIndependent of the containerDependent on the container
Relationship“Has-a” (weaker)“Part-of” (stronger)
ExamplesLibrary and BookCar and Engine

Benefits of Aggregation

  1. Flexibility: Objects can be easily shared and reused across different contexts.
  2. Loose Coupling: Reduces dependencies between classes, enhancing maintainability.
  3. Modularity: Promotes separation of concerns, making it easier to manage complex systems.

When to Use Aggregation

  • When the contained objects can exist independently of the container.
  • When you need to share objects between different classes.
  • When implementing relationships that do not imply exclusive ownership.

Practical Use Cases

  1. Modeling Real-World Relationships: Representing entities that naturally have independent existences, like departments and employees in an organization.
  2. Resource Management: Managing resources that can be shared, such as database connections or threads.
  3. GUI Development: Representing user interface components where widgets can exist independently of the window or dialog they are contained in.

Example: University and Students

#include <iostream>
#include <vector>
#include <string>

class Student {
public:
    std::string name;

    Student(const std::string& n) : name(n) {}

    void display() const {
        std::cout << "Student: " << name << std::endl;
    }
};

class University {
private:
    std::vector<Student*> students;

public:
    void addStudent(Student* student) {
        students.push_back(student);
    }

    void showStudents() const {
        for (const auto& student : students) {
            student->display();
        }
    }
};

int main() {
    Student s1("Alice");
    Student s2("Bob");

    University uni;
    uni.addStudent(&s1);
    uni.addStudent(&s2);

    uni.showStudents();

    return 0;
}