Composition

Composition is a design principle in object-oriented programming where a class contains objects of other classes as part of its state. It models a “has-a” relationship, meaning one class is composed of one or more objects from other classes.

Key Characteristics

  1. Ownership: The containing class (composite) owns the parts and is responsible for their lifecycle.
  2. Encapsulation: Encapsulates related objects, making them part of a larger whole.
  3. Reusability: Promotes code reuse by composing complex objects from simpler ones.

How Composition Works

In composition, the lifetime of the part objects is tied to the lifetime of the whole object. When the containing object is destroyed, its parts are also destroyed.

Example of Composition

#include <iostream>
#include <string>

class Engine {
public:
    void start() {
        std::cout << "Engine started." << std::endl;
    }
};

class Car {
private:
    Engine engine;  // Car has an Engine
    std::string model;

public:
    Car(const std::string& m) : model(m) {}

    void start() {
        engine.start();  // Delegating start to Engine
        std::cout << "Car model: " << model << " is ready to go!" << std::endl;
    }
};

int main() {
    Car myCar("Toyota");
    myCar.start();

    return 0;
}

Advantages of Composition

  1. Modularity: Breaks down complex systems into simpler, reusable components.
  2. Flexibility: Allows easy modification and extension of classes without affecting other parts.
  3. Encapsulation: Keeps related components together, promoting better encapsulation.

Difference Between Composition and Aggregation

  • Composition: Strong ownership. The contained object’s lifecycle is managed by the containing object (when the container is destroyed, so are the contained objects).
  • Aggregation: Weaker relationship. The contained objects can exist independently of the container.

Example of Aggregation

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

class Book {
public:
    std::string title;

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

class Library {
private:
    std::vector<Book*> books;  // Library has Books (Aggregation)

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

    void showBooks() {
        for (const auto& book : books) {
            std::cout << "Book: " << book->title << std::endl;
        }
    }
};

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

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

    library.showBooks();

    return 0;
}

Key Points

  • Composition:
    • Strong ownership.
    • Contained objects are destroyed with the container.
  • Aggregation:
    • Weaker relationship.
    • Contained objects can exist independently.

Benefits of Using Composition

  1. Code Reuse: Encourages the use of existing components.
  2. Improved Maintainability: Changes to parts of the system have minimal impact on other parts.
  3. Clearer Relationships: Models real-world relationships more accurately.