Skip to main content

Composition

Composition is aggregation with the leash removed: a whole made of parts, except this time the parts have no life of their own. A University consists of Departments, and if the university shuts down, so does every department in it - there is no "department without a university" in this model, full stop.

Definition: "has-a", with exclusive ownership

Where aggregation's parts are handed to the whole from outside, composition's parts are typically created by the whole, for the whole, and destroyed with it. The part's entire existence is scoped to its owner:

class University {
private String name;
private final List<Department> departments = new ArrayList<>();
 
void addDepartment(String deptName) {
departments.add(new Department(deptName, this)); // created here, by University
}
}

Notice the difference from aggregation's addProfessor(Professor p): that method received an already-existing object. This one calls new Department(...) itself. Nothing outside University ever gets a chance to hand it a pre-built Department, because a Department divorced from some university doesn't mean anything in this design.

UML notation

Composition draws as a solid line with a filled (solid black) diamond at the whole's end - same shape and position as aggregation's diamond, just filled in instead of hollow. That single filled-vs-hollow distinction is the one piece of UML notation people misread most often, precisely because the two diagrams otherwise look identical.

UniversityDepartment
A University is composed of Departments - filled diamond on the whole. Delete the university, the departments go with it.

Multiplicity

A University typically has one or more Departments (1 - 1..*, since a university with zero departments is an edge case worth allowing but rarely modeled as required), and - unlike aggregation - each Department belongs to exactly one university, never zero and never more than one. That "exactly one, permanently" is multiplicity quietly encoding the same exclusivity the filled diamond already states graphically.

Directionality

Composition is almost always modeled with the whole holding the parts, and it's common

  • more common than in aggregation - for the part to hold a back-reference to its owner, since the part is conceptually inseparable from it anyway (see the this passed into new Department(deptName, this) above). That back-reference doesn't carry the same synchronization risk called out on the association page: because University alone controls when departments are created or removed, there's only ever one place that has to keep the pointer correct.

Worked example

class Department {
private final String name;
private final University university; // set once, at construction, never reassigned
 
Department(String name, University university) {
this.name = name;
this.university = university;
}
}
 
class University {
private final String name;
private final List<Department> departments = new ArrayList<>();
 
University(String name) {
this.name = name;
}
 
void addDepartment(String deptName) {
departments.add(new Department(deptName, this));
}
 
void close() {
departments.clear(); // the University object is the only thing holding these;
// once dropped here, nothing else references them
}
}

The same shape shows up anywhere a whole is meaningless in pieces: an Order composed of LineItems (a line item referencing no order is just an orphaned quantity-and-price, not a business object), a TreeNode composed of its child TreeNodes, a Document composed of its Paragraphs. In every case, the constructor of the whole is the one and only place a part gets created.

Telling composition apart from its neighbours

The question that matters is the one from the aggregation page, just asked from the opposite direction: can the part be handed in from outside, or created independently and reused elsewhere? If yes, you're describing aggregation, not composition, no matter how tightly related the two classes feel. Department fails that test - nothing outside University calls new Department(...), and there is no addDepartment(Department d) overload waiting to accept a pre-built one. Professor, on the aggregation page, passes it easily - departments receive already-existing professors without ever constructing one.

Common mistakes

  • Exposing a constructor or setter that accepts a fully-built part from outside. The instant addDepartment(Department d) exists as a public method, an external caller can hand in a Department built (and potentially shared) elsewhere, and the relationship has quietly become aggregation - the diamond on the diagram is now lying.
  • Confusing "declared as a nested class" with composition. Composition is about lifecycle ownership, not syntax. A part stored in an external collection and passed around freely is not composed just because it happens to be a private field; a part referenced only via a builder pattern that hands the finished whole+parts back as one unit can still be genuine composition even though no code looks visually "nested."
  • Forgetting that composition can still have multiplicity greater than one on the part side. Composition is not "always one part" - a University can compose many Departments at once. The exclusivity is about ownership per part, not about how many parts there are.

Try it yourself: model a Car and its Engine. In your design, can an engine be removed from one car and installed in another? If the answer is yes, you've actually modeled aggregation - what would need to change about the code for it to become genuine composition instead?

Check yourself

Question 1 of 3

Two UML diagrams both show a diamond between University and Department, but one is filled and one is hollow. What is the practical difference?