Skip to content
IRC-CodingIRC-Coding
class relationshipsassociationaggregationcompositioninheritance

OOP Class Relationships: Association, Aggregation, Composition

Master OOP class relationships: association, aggregation, composition, and inheritance with practical examples and UML diagrams.

S

schutzgeist

6 min read
OOP Class Relationships: Association, Aggregation, Composition

OOP Class Relationships: Association, Aggregation, Composition & Inheritance

This guide provides a comprehensive overview of class relationships in OOP – covering association, aggregation, composition, and inheritance with practical examples.

In a Nutshell

Class relationships describe how objects interact and work together to form complex systems. The four primary relationship types are association, aggregation, composition, and inheritance.

Technical Overview

Class relationships define how classes and their objects are connected to one another. They’re fundamental to structuring object-oriented systems.

Four main relationship types:

1. Association (has-a)

  • Description: Two classes are linked to each other
  • Lifecycle: Independent of one another
  • Example: Customer has Orders
  • UML: Simple line between classes

2. Aggregation (composed of)

  • Description: A “has-a” relationship where parts can exist independently
  • Lifecycle: Parts can exist without the whole
  • Example: Car has Tires
  • UML: Line with empty diamond at the whole

3. Composition (part of)

  • Description: A “part-of” relationship where parts exist only with the whole
  • Lifecycle: Parts cannot exist without the whole
  • Example: Car has Engine
  • UML: Line with filled diamond at the whole

4. Inheritance (is-a)

  • Description: An “is-a” relationship indicating specialization
  • Lifecycle: Subclass inherits from superclass
  • Example: Car is a Vehicle
  • UML: Line with empty arrow pointing to superclass

Key Concepts

  • Association: Simple relationship between classes
  • Aggregation: “Has-a” relationship with independent parts
  • Composition: “Part-of” relationship with dependent parts
  • Inheritance: “Is-a” relationship with specialization
  • Multiplicity: 1, , 0..1, 1.., 0..*
  • UML Notation: Various arrows and diamond symbols
  • Lifecycle: Object dependency on one another
  • Architectural Importance: Critical for software design

Core Components

  1. Association: Bidirectional or unidirectional relationships
  2. Aggregation: Weak “has-a” relationship
  3. Composition: Strong “part-of” relationship
  4. Inheritance: Specialization and code reuse
  5. Multiplicity: Number of relationship instances
  6. Roles: Names describing the relationship
  7. Navigability: Direction of the relationship
  8. Qualifiers: Additional relationship information

Practical Examples

1. Association (Customer – Order)

// Bidirectional association
public class Kunde {
    private String kundenId;
    private String name;
    private List<Bestellung> bestellungen = new ArrayList<>();
    
    public void addBestellung(Bestellung bestellung) {
        bestellungen.add(bestellung);
        bestellung.setKunde(this); // Backreference
    }
    
    public List<Bestellung> getBestellungen() {
        return new ArrayList<>(bestellungen);
    }
}

public class Bestellung {
    private String bestellId;
    private Date bestelldatum;
    private Kunde kunde; // Backreference to customer
    
    public void setKunde(Kunde kunde) {
        this.kunde = kunde;
    }
    
    public Kunde getKunde() {
        return kunde;
    }
}

// Usage
Kunde meier = new Kunde("1", "Meier");
Bestellung b1 = new Bestellung("B001", new Date());
Bestellung b2 = new Bestellung("B002", new Date());

meier.addBestellung(b1);
meier.addBestellung(b2);

2. Aggregation (Car – Tires)

// Aggregation: Car has Tires, Tires can exist without Car
public class Auto {
    private String modell;
    private List<Reifen> reifen = new ArrayList<>();
    
    public Auto(String modell) {
        this.modell = modell;
    }
    
    public void addReifen(Reifen reifen) {
        if (reifen.size() < 4) {
            this.reifen.add(reifen);
        }
    }
    
    public void removeReifen(Reifen reifen) {
        this.reifen.remove(reifen);
        // Tire continues to exist and can be assigned to another car
    }
}

public class Reifen {
    private String hersteller;
    private int groesse;
    
    public Reifen(String hersteller, int groesse) {
        this.hersteller = hersteller;
        this.groesse = groesse;
    }
    
    // Tire can exist independently from the car
    public void montieren() {
        System.out.println("Reifen wird montiert");
    }
}

// Usage
Auto golf = new Auto("Golf");
Reifen michelin1 = new Reifen("Michelin", 195);
Reifen michelin2 = new Reifen("Michelin", 195);

golf.addReifen(michelin1);
golf.addReifen(michelin2);

// Tires can be removed and reused
golf.removeReifen(michelin1);

3. Composition (Car – Engine)

// Composition: Engine exists only with Car
public class Auto {
    private String modell;
    private Motor motor; // Engine cannot exist without Car
    
    public Auto(String modell, int leistung) {
        this.modell = modell;
        this.motor = new Motor(leistung); // Engine created internally
    }
    
    public void starten() {
        motor.starten();
        System.out.println(modell + " wird gestartet");
    }
    
    public void ausschalten() {
        motor.ausschalten();
        System.out.println(modell + " wird ausgeschaltet");
    }
    
    // Engine is destroyed with Car
    protected void finalize() {
        // Engine is automatically cleaned up
    }
}

public class Motor {
    private int leistung;
    private boolean laeuft;
    
    // Protected constructor - only Car can create Engine
    protected Motor(int leistung) {
        this.leistung = leistung;
        this.laeuft = false;
    }
    
    protected void starten() {
        this.laeuft = true;
        System.out.println("Motor mit " + leistung + " PS wird gestartet");
    }
    
    protected void ausschalten() {
        this.laeuft = false;
        System.out.println("Motor wird ausgeschaltet");
    }
}

// Usage
Auto bmw = new Auto("BMW", 200);
bmw.starten(); // Engine starts internally
bmw.ausschalten();

// Engine cannot be created independently:
// Motor motor = new Motor(150); // Error: Constructor is protected

4. Inheritance (Vehicle – Car)

// Superclass
public abstract class Fahrzeug {
    protected String marke;
    protected int baujahr;
    protected int aktuelleGeschwindigkeit = 0;
    
    public Fahrzeug(String marke, int baujahr) {
        this.marke = marke;
        this.baujahr = baujahr;
    }
    
    // Shared methods
    public void beschleunigen(int kmh) {
        this.aktuelleGeschwindigkeit += kmh;
        System.out.println(marke + " beschleunigt auf " + aktuelleGeschwindigkeit + " km/h");
    }
    
    public void bremsen(int kmh) {
        if (aktuelleGeschwindigkeit >= kmh) {
            this.aktuelleGeschwindigkeit -= kmh;
            System.out.println(marke + " bremst auf " + aktuelleGeschwindigkeit + " km/h");
        }
    }
    
    // Abstract method - must be implemented by subclasses
    public abstract void hupen();
    
    // Concrete method - can be overridden
    public void anzeigen() {
        System.out.println("Fahrzeug: " + marke + ", Baujahr: " + baujahr + 
                          ", Geschwindigkeit: " + aktuelleGeschwindigkeit + " km/h");
    }
}

// Subclass
public class Auto extends Fahrzeug {
    private int anzahlTueren;
    private boolean klimaanlage;
    
    public Auto(String marke, int baujahr, int anzahlTueren) {
        super(marke, baujahr); // Call superclass constructor
        this.anzahlTueren = anzahlTueren;
        this.klimaanlage = false;
    }
    
    // Implementation of abstract method
    @Override
    public void hupen() {
        System.out.println("Auto hupt: Tut Tut!");
    }
    
    // Additional method specific to Car
    public void klimaanlageEin() {
        klimaanlage = true;
        System.out.println("Klimaanlage eingeschaltet");
    }
    
    // Override superclass method
    @Override
    public void anzeigen() {
        super.anzeigen(); // Call superclass method
        System.out.println("  Typ: Auto, Türen: " + anzahlTueren + 
                          ", Klimaanlage: " + (klimaanlage ? "an" : "aus"));
    }
}

// Additional subclass
public class Motorrad extends Fahrzeug {
    private boolean hatSeitenwagen;
    
    public Motorrad(String marke, int baujahr, boolean hatSeitenwagen) {
        super(marke, baujahr);
        this.hatSeitenwagen = hatSeitenwagen;
    }
    
    @Override
    public void hupen() {
        System.out.println("Motorrad hupt: Iiih Iiih!");
    }
    
    public void wheelie() {
        System.out.println("Motorrad macht Wheelie!");
    }
    
    @Override
    public void anzeigen() {
        super.anzeigen();
        System.out.println("  Typ: Motorrad, Seitenwagen: " + 
                          (hatSeitenwagen ? "ja" : "nein"));
    }
}

// Usage
Fahrzeug golf = new Auto("Volkswagen", 2023, 5);
Fahrzeug harley = new Motorrad("Harley-Davidson", 2022, false);

golf.hupen();      // Auto hupt: Tut Tut!
harley.hupen();   // Motorrad hupt: Iiih Iiih!

golf.beschleunigen(50);
harley.beschleunigen(80);

golf.anzeigen();
harley.anzeigen();

// Downcasting for specific methods
if (golf instanceof Auto) {
    Auto autoGolf = (Auto) golf;
    autoGolf.klimaanlageEin();
}

if (harley instanceof Motorrad) {
    Motorrad motorradHarley = (Motorrad) harley;
    motorradHarley.wheelie();
}

UML Notation for Relationships

Association

Customer 1..* --* Order

Aggregation

Car 1 --* Tire

Composition

Car 1 --* Engine

Inheritance

Vehicle <|-- Car
Vehicle <|-- Motorcycle

Multiplicity

SymbolMeaningExample
1Exactly one1 Engine
0..1Zero or one0..1 License
*Zero or more* Tires
1..*One or more1..* Doors
2..4Between 2 and 42..4 Wheels

Choosing the Right Relationship Type

When to use which relationship?

Use association when:

  • Two classes interact but have no dependency
  • The relationship is temporary or optional
  • Objects exist independently of each other

Use aggregation when:

  • A “has-a” relationship exists
  • Parts can exist without the whole
  • Parts can be shared among different wholes

Use composition when:

  • A “is-part-of” relationship exists
  • Parts exist only as part of the whole
  • The lifecycle of the part is tied to the whole

Use inheritance when:

  • An “is-a” relationship exists
  • A subclass is a specialized form of the superclass
  • Code reuse and polymorphism are desired

Advantages and Disadvantages

Benefits of class relationships

  • Structure: Clear system architecture
  • Reusability: Shared functionality across classes
  • Flexibility: Easy to extend and modify
  • Clarity: Models the real world accurately
  • Maintainability: Targeted changes are straightforward

Drawbacks

  • Complexity: Many relationships can become hard to follow
  • Coupling: Strong dependencies can create problems
  • Performance: Too many object connections can slow things down
  • Testability: Complex relationships are harder to test

Common Exam Questions

  1. What’s the difference between aggregation and composition? With aggregation, parts can exist independently; with composition, parts exist only as part of the whole.

  2. Explain the multiplicity 1..*! One or more objects can be connected—at least one is required, but there’s no upper limit.

  3. When do you use inheritance instead of composition? When an “is-a” relationship exists and you want to reuse code.

  4. What does bidirectional association mean? Both classes know about each other and can access each other’s members.

Key Resources

  1. https://de.wikipedia.org/wiki/Assoziation_(UML)
  2. https://refactoring.guru/design-patterns/composition-over-inheritance
  3. https://www.uml-diagrams.org/class-diagram-relationships.html

Next in the OOP Learning Path

The next article in the OOP learning path covers OOP Inheritance: Fundamentals, Inheritance & Polymorphism — how inheritance enables code reuse.

Back to Blog
Share:

Related Posts