Composition

Composition represents a strong whole-part relationship. The whole exclusively owns and manages its parts.

Composition is a strong whole-part relationship in which the whole owns its parts and controls their lifecycle.

A part normally cannot meaningfully exist without its whole.

Composition means strong “has-a” ownership: if the whole disappears, its parts conceptually disappear with it.

Simple Example

Consider an Order containing OrderItem objects.

class OrderItem {
    private String product;
    private int quantity;

    OrderItem(String product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }
}

class Order {
    private List<OrderItem> items = new ArrayList<>();

    void addItem(String product, int quantity) {
        items.add(new OrderItem(product, quantity));
    }
}

The Order creates and owns its order items.

An OrderItem belongs to one specific order and has no meaningful independent lifecycle.

UML Notation

Composition uses a filled diamond on the side of the whole:

Here:

  • Order is the whole.
  • OrderItem is the part.
  • The filled diamond appears beside Order.

Multiplicity

Multiplicity shows how many parts the whole can contain:

This means one order contains one or more order items.

Main Characteristics

  • It represents a strong whole-part relationship.
  • The whole owns its parts.
  • A part normally belongs to only one whole.
  • The part depends on the whole for its lifecycle.
  • The part usually cannot be shared between multiple wholes.
  • Destroying the whole conceptually destroys its parts.
  • UML represents it with a filled diamond.

More Examples

Possible examples include:

  • An order contains order items.
  • A house contains rooms.
  • A document contains pages.
  • A questionnaire contains questions.
  • A user interface window contains internal controls.

These examples assume the parts are created for and owned by one particular whole.

Composition vs Aggregation

The main difference is ownership and lifecycle.

Aggregation

The part can exist independently:

Team <>- Player

A player can exist without a particular team and can move to another team.

Composition

The part depends on the whole:

Order <#>- OrderItem

An order item belongs to one order and has little meaning without it.

AggregationComposition
Weak ownershipStrong ownership
Part exists independentlyPart depends on the whole
Part may be sharedPart normally belongs to one whole
Whole does not control the part’s lifecycleWhole controls the part’s lifecycle
Hollow diamondFilled diamond
Deleting the whole leaves the parts validDeleting the whole removes its parts conceptually

Composition vs Inheritance

Composition represents a has-a relationship:

Car has an Engine.

Inheritance represents an is-a relationship:

ElectricCar is a Car.

Composition builds behaviour by combining objects. Inheritance builds behaviour by extending a parent class.

A common design principle is:

Prefer composition over inheritance when behaviour can be assembled using smaller, independent components.

Composition often provides greater flexibility because an object can delegate work to another object without becoming tightly connected to a parent-class hierarchy.

However, this principle does not mean inheritance is always wrong. Inheritance is appropriate when a genuine and stable “is-a” relationship exists.

How to Recognise Composition

Ask these questions:

  1. Is one object a meaningful part of another?
  2. Does the whole exclusively own the part?
  3. Can the part belong to multiple wholes?
  4. Can the part meaningfully exist without the whole?
  5. Should removing the whole also remove its parts?

If the part is exclusively owned and lifecycle-dependent, composition is likely appropriate.

For an OrderItem:

  • It is created for a specific order.
  • It belongs to only that order.
  • It should not be shared between orders.
  • Removing the order should remove its items.

Therefore, the relationship is composition.

Implementation Approaches

The whole creates its parts

class House {
    private final List<Room> rooms = new ArrayList<>();

    void addRoom(String name) {
        rooms.add(new Room(name));
    }
}

This clearly communicates ownership.

The parts are not exposed for uncontrolled modification

class Order {
    private final List<OrderItem> items = new ArrayList<>();

    List<OrderItem> getItems() {
        return Collections.unmodifiableList(items);
    }
}

This prevents outside code from directly changing the owned collection.

Database-level ownership

Composition may also appear in a database:

orders
order_items

Each order_items row references one order. Deleting an order may delete its items using a cascading foreign-key rule.

Important Lifecycle Note

“Destroying the whole destroys the parts” describes the conceptual model.

The exact technical behaviour depends on the programming language and storage system:

  • A garbage collector may remove unreachable objects later.
  • A database may require cascade deletion.
  • External resources may require explicit cleanup.
  • Other references can accidentally keep an object alive in memory.

The design must enforce the intended ownership rules.

When to Use Composition

Use composition when:

  • A part is created specifically for one whole.
  • The whole has exclusive ownership.
  • The part should not be shared.
  • The part has no meaningful independent lifecycle.
  • The whole should control access to and modification of the part.
  • Removing the whole should also remove its parts.

Benefits of Composition

  • Makes ownership clear.
  • Keeps related behaviour together.
  • Protects internal implementation details.
  • Reduces accidental sharing of parts.
  • Helps enforce lifecycle rules.
  • Can create flexible behaviour through object delegation.
  • Often avoids rigid inheritance hierarchies.

Common Mistakes

Using composition when parts should be independent

If a part can move between different wholes or exist by itself, aggregation may be more accurate.

Sharing composed parts

A composed part normally belongs exclusively to one whole. Sharing it weakens the ownership model.

Exposing mutable internal collections

Returning an editable collection allows outside code to modify owned parts without the whole’s control.

Assuming a field automatically means composition

The following code only proves that Car references an Engine:

class Car {
    Engine engine;
}

To identify composition, we must understand who creates, owns and removes the engine.

Confusing object composition with UML composition

“Prefer composition over inheritance” broadly means building behaviour by combining objects. UML composition more specifically describes strong ownership and lifecycle dependency.

Interview Explanation

A short answer:

Composition is a strong whole-part relationship in which the whole exclusively owns its parts and controls their lifecycle. The parts normally cannot meaningfully exist without the whole. UML represents it using a filled diamond.

A stronger answer:

In composition, a part belongs to one whole and is lifecycle-dependent on it. For example, an order owns its order items. If the order is removed, its items should also be removed. This differs from aggregation, where the parts can continue to exist independently.

Interview Questions

  1. What is composition in object-oriented design?
  2. Why is composition considered strong ownership?
  3. Can a composed object exist independently?
  4. Can a composed part belong to multiple wholes?
  5. What UML symbol represents composition?
  6. Where is the filled diamond placed?
  7. How does composition differ from aggregation?
  8. How does composition differ from inheritance?
  9. What does “prefer composition over inheritance” mean?
  10. Does a programming language automatically enforce composition?

Key Takeaways

  • Composition represents a strong whole-part relationship.
  • The whole exclusively owns and manages its parts.
  • Parts are normally lifecycle-dependent on the whole.
  • A composed part usually belongs to only one whole.
  • UML uses a filled diamond beside the whole.
  • Composition is stronger than aggregation.
  • It should be used when ownership and lifecycle dependency are important.
  • The intended lifecycle must still be enforced in code or storage.