﻿# UML relationships: association, aggregation, composition, inheritance

> The line ends of a UML class diagram and the Java each one means, read on a zoo and designed on a role-playing game where subclasses override one damage rule – and why that rule is better abstract than defaulted.

Source: https://laszlonemes.com/university/programming-technology/uml-relationships · Author: László Nemes

Session 2 · Programming Technology

The line ends of a UML class diagram and the Java each one means, read on a zoo and designed on a role-playing game where subclasses override one damage rule – and why that rule is better abstract than defaulted.

16 min read `Association, aggregation, composition` `Multiplicity as field types` `Abstract classes and methods` `Overriding and dynamic dispatch` `Constructor chaining`

A program of ten classes is already too big to keep in your head as code. A UML class diagram is the map: a box per class, a line per relationship, and the shape of the line end says what kind of relationship it is. This session goes through those line ends – association, aggregation, composition, inheritance – and what each one means in Java. Then we read one diagram (a zoo) and design one (a role-playing game) where inheritance and method overriding do the real work.

> **Every line in a class diagram is a decision you will have to code:** who holds a reference to whom, who creates whom, and which class decides a rule. If you can't draw the line, you don't know yet what to write.

After this session you can

- read and draw the five relationship line ends of a UML class diagram
- tell aggregation from composition by asking who creates the part and who else refers to it
- turn a multiplicity (1, 0..1, *, 1..*) into the right field type
- design a class hierarchy where subclasses override one protected method
- decide when a method should be abstract instead of having a default body

## Why draw before coding

The class diagram shows *structure*: the classes, their fields and methods, and how they are connected. It leaves out what the methods do. That is exactly the right level for the first design decisions – which classes exist, which one owns which data – and it is cheap to change. Moving a field from one box to another is an eraser stroke; moving it in finished code is an afternoon.

- **+ public** – Visible to every class.
- **# protected** – Visible to subclasses (and, in Java, to the same package).
- **- private** – Visible only inside the class.
- ***italic name*** – An abstract class or method – no objects / no body. I also write «abstract» above the name.
- **name : Type** – A field or a return type: HP : int, isAlive() : boolean.

In your assignment

**The diagram goes into the documentation**

Each assignment's documentation contains the program's UML class diagram. Draw it *first*, as a plan, and update it at the end so it matches the code. A diagram generated from the code afterwards shows every getter and none of the thinking – I'd rather see the fields and the methods that carry the rules, and correct line ends.

## Five relationships, five line ends

All relationships between classes boil down to five kinds, and each has its own line end (Fig. 1). The first three are all "A has B" – they differ in how strongly A owns the B.

> **Fig. 1** · The line end carries the meaning. A diamond always sits on the whole, a triangle always on the parent, an arrowhead on the class being used.
>
> *Diagram:* The five relationships of a UML class diagram and the Java they mean. Association, an open arrow: A has a field of type B. Aggregation, a hollow diamond on the whole: A holds Bs created elsewhere. Composition, a filled diamond: A creates and owns its Bs. Inheritance, a hollow triangle on the parent: B extends A. Dependency, a dashed open arrow: A only uses a B in a method.

An **association** is the general case: the two classes know each other, nothing is said about ownership. A bank and its employees – the bank has a set of employees, the employees were hired, not created by the bank.

Association: a field, nothing more

```java
class Bank {
    private String bankName;
    private Set<Employee> employees;     // Bank knows its employees …

    public void setEmployees(Set<Employee> employees) {
        this.employees = employees;      // … but did not create them
    }
}
```

**Inheritance** (generalization) is a different kind of relationship: not "has a" but "is a". A `Lion` is an `Animal`, so anywhere an `Animal` is expected, a `Lion` will do. A **dependency** is the weakest link: a class uses another one inside a method – a parameter, a local variable, a thrown exception – without keeping a reference.

## Aggregation vs composition

Both are "whole–part" relationships, and students mix them up more than anything else on this page. The question that separates them is about **lifetime**: can the part live without the whole?

- **Aggregation** (hollow diamond): the parts exist independently. A department has students, but the students were created elsewhere and handed to it; closing the department doesn't delete the students.
- **Composition** (filled diamond): the whole creates and owns its parts. A company's departments make no sense without the company; nobody outside holds them.

Aggregation: the parts come from outside

```java
// main creates the students – they exist on their own
List<Student> cseStudents = new ArrayList<>();
cseStudents.add(new Student("Parul", 1));
cseStudents.add(new Student("Sachin", 2));

// the department is handed parts that already exist
Department cse = new Department("CSE", cseStudents);
```

Composition: the whole creates its parts and guards them

```java
class Company {
    private final List<Department> departments = new ArrayList<>();

    public void addDepartment(String name) {
        departments.add(new Department(name));   // the whole creates its parts
    }

    public List<Department> getDepartments() {
        return new ArrayList<>(departments);     // a copy: outsiders can't add or remove
    }
}
```

In Java nothing is ever "destroyed" by hand – an object disappears when nothing refers to it any more, and the garbage collector reclaims it. So in Java terms, composition means: the whole is the *only* one holding references to its parts (Fig. 2). That is why `getDepartments()` returns a copy of the list – handing out the real list would let anyone add or remove departments behind the company's back.

> **Fig. 2** · Remove the whole and see what is still reachable. Aggregated parts have other references and survive; composed parts had only the whole.
>
> *Diagram:* What survives when the whole is removed. Aggregation: the Department is gone, but its two Student objects are still referenced by a list in main, so they live on. Composition: the Company is gone, and its Department objects were only referenced by the company, so they become unreachable and the garbage collector removes them.

Good to know

**When in doubt, ask two questions**

Who calls `new` for the part? And does anyone else keep a reference to it? "The whole, and nobody else" is composition. Anything else is aggregation or plain association.

## Multiplicity becomes a field type

The numbers at the line ends say how many objects take part. They translate directly into the type of the field and into a rule the constructor has to keep.

| Multiplicity | Meaning | In Java |
| --- | --- | --- |
| 1 | exactly one | `Restaurant restaurant` – never null, set in every constructor |
| 0..1 | one or none | a field that may be `null` – check before use |
| * | any number, also zero | `List<Animal> animals` – may be empty |
| 1..* | at least one | a list, and the constructor refuses an empty one |

## Reading a diagram: the zoo

The zoo example is a small "simulation": a zoo with animals and a restaurant, and a caretaker who looks after some of the animals (Fig. 3). Read it top-down: first the relationships around `Animal`, then the hierarchy below it.

> **Fig. 3** · The zoo. Composition for the restaurant (the zoo builds it), aggregation for the animals (they are passed in), association for the caretaker, and a two-level hierarchy below the abstract Animal.
>
> *Diagram:* The zoo's class diagram. Zoo is composed of one Restaurant and aggregates one or more Animals; a CareTaker is associated with the Animals it takes care of. Animal is abstract with move and makeSound. Pet and WildAnimal are abstract subclasses; Cat, Dog and Hamster extend Pet, Lion and Elephant extend WildAnimal.

`Animal` is `abstract`: there is no "just an animal" in the zoo, so it can't be instantiated. It gives every animal a default `move()` and declares `makeSound()` without a body – every concrete animal has to say how it sounds. `Pet` overrides `move()` ("moves lazily"), so a `Cat`, which doesn't override it, moves like a pet; a `Dog` overrides it again and runs.

Animal.java

```java
public abstract class Animal {

    public void move() {                  // a default every animal inherits
        System.out.println("Animal moves");
    }

    public abstract void makeSound();     // no body: each concrete animal must say how
}
```

The payoff is in `Zoo.step()`: one loop over `Animal` references, and every object answers with its own behaviour. Adding a `Giraffe` later needs a new class and nothing else – `step()` doesn't change. This is **polymorphism**.

Zoo.java – one loop, many behaviours

```java
public void step() {
    for (Animal a : animals) {   // static type: Animal
        a.move();                // runs Dog.move(), Lion.move(), Pet.move() …
        a.makeSound();           // abstract in Animal – every concrete class has one
    }
}
```

Common mistake

**The constructor that forgets a part**

The diagram says every zoo has exactly one restaurant (composition, multiplicity 1). The code disagrees: the second constructor copies the animals but never creates the restaurant, so a zoo built that way has `restaurant == null`. Composition is a promise *every* constructor has to keep – the easy way is to let one constructor call the other.

The bug

```java
public class Zoo {

    private ArrayList<Animal> animals;   // aggregation: animals come from outside
    private Restaurant restaurant;       // composition: the zoo makes its own

    public Zoo() {
        animals = new ArrayList<>();
        restaurant = new Restaurant();
    }

    public Zoo(ArrayList<Animal> animals) {
        this.animals = new ArrayList<>(animals);   // copies the list, shares the animals
        // ← no restaurant here: this zoo's restaurant is null
    }
}
```

The fix: constructor chaining with this(…)

```java
public Zoo() {
    this(new ArrayList<>());           // delegate to the other constructor
}

public Zoo(List<Animal> animals) {
    this.animals = new ArrayList<>(animals);
    this.restaurant = new Restaurant();   // every Zoo gets its restaurant, whichever constructor ran
}
```

Common mistake

**A public list is not aggregation, it's an open door**

`CareTaker` declares `public ArrayList<Animal> animals`. Any class can now clear or replace the list, and the `addAnimal` / `removeAnimal` methods guard nothing. Keep the collection private and expose only the operations you mean.

## Designing a hierarchy: the role-playing game

The exercise of this session: in a role-playing game a character is the main character, an orc or a dragon. Orcs are fighters, berserkers or defenders; a dragon is red or black. Every character has a name, hit points (HP) and an attack value, and can attack another character, whose HP drops by the attacker's attack value – except that

- a berserker loses twice the attack value, a defender only half,
- a black dragon is hurt only if the attack is greater than 20, a red dragon only above 60,
- the main character has a real-valued defense, and the attack is divided by it,
- a character is alive exactly when its HP is 0 or more.

The design question is: *which part of the behaviour differs per class?* Not attacking – every character attacks the same way. Only the damage rule differs. So the common protocol lives in `Character`, and the varying step is one protected method, `applyDamageFrom`, that subclasses override (Fig. 4).

> **Fig. 4** · One method varies, so one method is overridden. Orc and Dragon are abstract groupings; Dragon also holds the rule shared by both colours, and the colours only supply the threshold.
>
> *Diagram:* The role-playing game's class diagram. The abstract Character has name, HP and attack, the public attack, attacked and isAlive methods and a protected applyDamageFrom. MainCharacter adds a defense value, abstract Orc has Fighter, Berserker and Defender, abstract Dragon adds an attack threshold and has RedDragon with threshold 60 and BlackDragon with threshold 20. Every class that takes damage differently overrides applyDamageFrom.

Character.java – the fixed protocol and the variable step

```java
public void attack(Character character) {
    if (isAlive()) {
        character.attacked(this);          // "I hit you"
    }
}

public void attacked(Character character) {
    if (isAlive()) {
        applyDamageFrom(character);        // the one step that differs per class
    }
}

protected void applyDamageFrom(Character character) {
    HP -= character.attack;                // the default rule
}
```

Follow one hit through the objects (Fig. 5). `main` only knows it has two `Character`s. Which `applyDamageFrom` runs is decided at run time by the class of the object that receives the call – this is **dynamic dispatch**.

> **Fig. 5** · attack → attacked → applyDamageFrom. The first two methods are the same for everyone; the third is the hook each subclass fills in.
>
> *Diagram:* A sequence diagram of one hit. Game.main calls attack(orc1) on the ranger. The ranger, if alive, calls attacked(ranger) on orc1. orc1, if alive, calls applyDamageFrom(ranger) on itself, and because orc1 is a Berserker, the Berserker's version runs and subtracts twice the ranger's attack from its HP.

Two overrides

```java
// Berserker: double damage
@Override
protected void applyDamageFrom(Character character) {
    HP -= character.attack * 2;
}

// Dragon: hurt only above its threshold (20 for black, 60 for red)
@Override
protected void applyDamageFrom(Character character) {
    if (character.attack > ATTACK_THRESHOLD) {
        HP -= character.attack;
    }
}
```

The dragons show a second trick. The rule "hurt only above a threshold" is the same for both colours, so it is written once, in `Dragon`. The colours differ only in a number, and a number is data, not behaviour – so `RedDragon` and `BlackDragon` just pass it to the parent constructor.

Dragon.java, RedDragon.java

```java
public abstract class Dragon extends Character {

    protected final int ATTACK_THRESHOLD;

    public Dragon(String name, int HP, int attack, int threshold) {
        super(name, HP, attack);           // the Character part first
        this.ATTACK_THRESHOLD = threshold;
    }
}

public class RedDragon extends Dragon {
    public RedDragon(String name, int HP, int attack) {
        super(name, HP, attack, 60);       // the subclass only fixes the number
    }
}
```

In your assignment

**Same pattern, first assignment**

The first assignment is built exactly like this: a few kinds of things (creatures, vehicles, animals) that follow the same rules with different numbers. Put the rules in an abstract base class *once*, and let each subclass supply only what differs – ideally through small abstract methods such as `waterChange(Weather)` or `capacity()`. If two subclasses contain the same `if`, it belongs in the parent.

Common mistake

**One class with a type field**

The tempting shortcut is a single `Character` class with a `String type` and a `switch (type)` in `applyDamageFrom`. It works until the next kind of character arrives, and then every switch in the program has to change. If behaviour depends on the kind of object, the kind should be a class.

## Abstract methods: no silent defaults

In the first version, `Character` has a default damage rule (`HP -= attack`), and `Fighter` uses it without saying so. The second version, `rpgame_abstract`, makes `applyDamageFrom` abstract: there is no default, so every concrete class must state its rule, and forgetting one is a compile error instead of a silent bug (Fig. 6).

> **Fig. 6** · A default body is convenient; an abstract method is explicit. Choose abstract when there is no sensible rule that fits “any” subclass.
>
> *Diagram:* Two versions of the same hierarchy. In rpgame, Character has a default applyDamageFrom and Fighter silently inherits it. In rpgame_abstract, applyDamageFrom is abstract in Character, so Fighter must implement its own rule or the program does not compile.

rpgame_abstract – Character.java

```java
public abstract class Character {
    // …
    private void attacked(Character character) {         // private: only attack() may call it
        if (isAlive()) {
            applyDamageFrom(character);
        }
    }

    protected abstract void applyDamageFrom(Character character);   // no default any more
}
```

The same version also makes `attacked` `private`. It was public before, so `main` could call `orc.attacked(dragon)` and deal damage without an attack ever happening. Now the only way in is `attack()`, which checks that the attacker is alive. Make public only what other classes are supposed to call.

Good to know

**abstract class vs interface**

An abstract class can hold fields and finished methods (the protocol, the HP). An interface (next sessions: `Comparable`, `ActionListener`) is a pure promise of methods, and a class can implement many of them. Use an abstract class for "a kind of" with shared state, an interface for "can do".

## Practice

Implement the role-playing game from the description above, step by step.

Task 1

**Draw the class diagram first**

On paper or in any UML tool, draw the classes, their fields and methods, and the inheritance lines. Mark which classes are abstract and which method varies.

**Where to start:**

Find the nouns that are "kinds of" something else (orc, dragon, berserker …) – those are the hierarchy. Then find the one sentence of the task that says something differs per kind – that is the method to override.

Task 2

**The Character base class**

Write the abstract `Character` with name, HP, attack, a constructor, `attack`, `attacked`, `isAlive` and the protected damage method.

**Keep the protocol in one place:**

`attack(other)` calls `other.attacked(this)`; `attacked` calls `applyDamageFrom`. Only the last one will ever be overridden. Make the fields `protected` so subclasses can change `HP`.

Task 3

**The subclasses**

Add `MainCharacter`, the abstract `Orc` with `Fighter`, `Berserker`, `Defender`, and the abstract `Dragon` with `RedDragon` and `BlackDragon`.

**The main character's defense:**

Defense is a `double`. Dividing an `int` by a `double` gives a `double`, and `-=` quietly converts the result back to `int` – fine here, but know it happens.

```java
@Override
protected void applyDamageFrom(Character character) {
    HP -= character.attack / defense;   // defense is a double
}
```

**The dragons:**

Put the threshold check in `Dragon`. Each colour only calls `super(…)` with its own number.

Task 4

**The melee**

In `main`, put several characters into one list. Pick a random attacker and a different random target, let them fight, and remove whoever dies. Repeat until one character is left and print the winner. Use `Random.nextInt`.

**Loop skeleton:**

```java
Random rand = new Random();
while (characters.size() > 1) {
    int attackerInd = rand.nextInt(characters.size());
    int attackedInd = rand.nextInt(characters.size());
    // … pick again while the two indexes are equal
    // … attack, and remove the attacked one if it died
}
```

Remove by index, after the attack. The list's static type is `List<Character>` – this is the zoo's polymorphic loop again.

**Watch the name clash:**

`java.lang.Character` is imported automatically. Your own `Character` class in the same package wins, but the clash is confusing in error messages – `GameCharacter` is a fine alternative name.

Task 5

**Make the rule abstract**

Change `applyDamageFrom` to abstract in `Character` and make `attacked` private. Fix every compile error that follows. Which classes needed a change, and why exactly those?

**What to expect:**

Only the concrete classes that relied on the default break: `Fighter` (and nobody else, because `Dragon` already supplies the rule for both colours). The abstract `Orc` may stay without one.

Task 6

**Fix the zoo**

Make every `Zoo` have its restaurant regardless of the constructor, and make `CareTaker`'s list private. Then add a new animal type without touching `Zoo`.

**Approach:**

One constructor does the work, the other calls it with `this(…)`. The new animal extends `Pet` or `WildAnimal` and overrides `makeSound()` – the compiler insists.

## Summary

- Association: A has a field of type B. Aggregation (hollow diamond): A holds parts made elsewhere. Composition (filled diamond): A creates and solely owns its parts. Inheritance (triangle): B is an A. Dependency (dashed arrow): A uses B in a method.
- In Java, composition means the whole is the only holder of references to its parts – create them inside, and hand out copies.
- Multiplicities become field types and constructor rules: 1 is never null, * is a possibly empty list, 1..* is a list the constructor guards.
- Put the common protocol in the base class and let subclasses override one protected method; dynamic dispatch picks the right one at run time.
- Make a method abstract when there is no sensible default – forgetting it becomes a compile error, not a bug.
