UML Relationships in Java

5 minute read

Conceptual overview (UML basics)

Association -> Aggregation -> Composition

Wikipedia : Sometimes aggregation is referred to as composition when the distinction between ordinary composition and aggregation is unimportant.

Association: “can-call” - connection between classes, like a friendship.

Aggregation: “has-a” relationship, where one class has (but doesn’t own) instances of another class.

Composition: “composed of” relationship (Whole-Part), where one class is composed of (and owns) instances of another class.


Association (concept)

https://www.uml-diagrams.org/association.html?context=class-diagrams

  • General relationship
  • No specific ownership or lifecycle dependency
  • One-to-one, one-to-many, or many-to-many
  • Represented by a simple line connecting classes

For example, a Department class may be associated with an Employee class to represent that employees belong to different departments.

Association (one-way) + multiplicity

public class Department {
    private List<Employee> employees;

    public Department(List<Employee> employees) {
        this.employees = employees;
    }
}

@Data
public class Employee {
    private String name;
    private int id;
}

Association (two-way)

Code snippet (bidirectional link)

public class User {
    private Email email;

    public User(Email email) {
        this.email = email;
    }
}

public class Email {
    private User recipient;

    public Email(User recipient) {
        this.recipient = recipient;
    }
}

N-ary association

Code snippet (three participants)

public class Meeting {
    private final Person host;
    private final Room room;
    private final CalendarDay day;

    public Meeting(Person host, Room room, CalendarDay day) {
        this.host = host;
        this.room = room;
        this.day = day;
    }
}

Has-A relationship: composition vs. aggregation (concept)

Composition and Aggregation both represent a “has-a” relationship between classes, but they differ in ownership and dependency.

Aggregation (empty diamond)

  • Special form of association
  • “Whole-part” relationship
  • Weaker ownership: The part class is less tightly bound to the whole class.
  • Dependency: Parts can exist independently and be shared among multiple containing classes.
  • Example:
    • A University “has-a” Department. Departments may continue to exist independently even if the University closes.
    • A Car class may have wheels, where wheels are parts of the car but can exist independently.
// Contained class
public class Wheel {
    // Attributes and methods of the wheel
}

// Containing class
public class Car {
    private final List<Wheel> wheels; // Aggregation relationship

    public Car(List<Wheel> wheels) {
        this.wheels = wheels; // Assigning wheels passed as parameter
    }
}

Composition (filled diamond)

  • Also known as whole-part Has-a relationship
  • Strong ownership: The part class is tightly bound to the whole class.
  • Part class’s lifecycle is controlled by the whole class
  • Indicated by a filled diamond (◆)

A Car class may have an Engine, where the Engine is a part of the Car and cannot exist independently. If the Car is destroyed, the Engine ceases to exist.

// Contained class
public class Engine {
    // Attributes and methods of the engine
}

// Containing class
public class Car {
    private final Engine engine; // Composition relationship

    public Car() {
        this.engine = new Engine(); // Creating engine instance within Car
    }
}

A House class may have rooms, where rooms are parts of the house and cannot exist without the house. If the House is destroyed, Rooms cease to exist.

public class House {
    private List<Room> rooms;

    public House() {
        this.rooms = new ArrayList<>();
    }
}

Multiplicity

  • Denotes the number of instances of one class related to the number of instances of another class.
  • Represented with a number at one end of the line and a “*” (asterisk) at the other end.

// Example of multiplicity in a relationship
public class Department {
    private final List<Employee> employees; // One-to-many relationship

    public Department(List<Employee> employees) {
        this.employees = employees;
    }
}

Generalization - IS-A (empty arrowhead)

Generalization/Inheritance represents an “is-a” relationship, where the subclass inherits attributes and behaviors from the superclass.

  • Denoted by an empty arrowhead pointing from the subclass to the superclass.
  • Indicates an “is-a” relationship.
  • IS-A: The property of an object being an instance of a data type.
  • Subclass inherits attributes and behaviors from the superclass.

Generalization/Inheritance (implements)

  • The Is-A relationship test is also known as INHERITANCE test.
  • This holds true for a child that is a subclass of any parent, be it a direct subclass or a distant child.
// Parent class
public class Vehicle {
    // Attributes and methods common to all vehicles
    public Vehicle(String manufacturer, int year) { }
}

// Child class inheriting from Vehicle
public class Car extends Vehicle {
    // Additional attributes and methods specific to cars
    public Car(String manufacturer, int year, String model, int mileage) {
        super(manufacturer, year);
    }
}

public class Boat extends Vehicle {
  public Boat(String manufacturer, int year, String type, int length) {
    super(manufacturer, year);
  }

Generalization/realization (extends)

  • We use the multi-inheritance property of interfaces to preserve the IS-A relationship.
    • For example, a Cat is an Animal, and a Cat is also a Pet.

Code snippet (interface realization)

public interface Animal {
    void eat();
    void sleep();
}

public interface Pet {
    void play();
    void cuddle();
}

public class Cat implements Animal, Pet {
    public void eat() { }
    public void sleep() { }
    public void play() { }
    public void cuddle() { }
}


Dependency (dashed line with arrow)

  • Denoted by a dashed line with an arrow pointing from the dependent class to the independent class.
  • Represents a using or “uses-a” relationship, where one class depends on another class for its implementation.
  • Dependencies are typically indicated by method parameters, local variables, or return types.

    // Dependent class
    public class Car {
        public void drive(Engine engine) { // Dependency on Engine class
            // Implementation of drive method using Engine
        }
    }
    

Code snippet (constructor injection + usage)

private final Engine engine;

public Car(Engine engine) {
    this.engine = engine;
}

public void drive() {
    engine.start();
}

Using « » for Annotations:

  • Used to indicate properties or qualifiers of a relationship or dependency.
  • Often used with dependencies to specify the nature of the relationship, such as «interface», «implementation», «association», etc.

For example, if class A implements interface B, you would write “«implements»” near the dashed line between class A and interface B.

// Dependent class implementing an interface
public class Car implements Drivable { // <<implements>> annotation
    // Implementation of methods from Drivable interface
}

Elaborate end-to-end model

public class Car extends Vehicle implements Drivable {
    private List<Wheel> wheels;
    private Engine engine;

    public Car(String manufacturer, String model, List<Wheel> wheels, Engine engine) {
        super(manufacturer, model);
        this.wheels = wheels;
        this.engine = engine;
    }
}

public class Boat extends Vehicle implements Drivable {
    private Engine engine;
    private Rudder rudder;

    public Boat(String manufacturer, String model, int length, String type, Engine engine, Rudder rudder) {
        super(manufacturer, model);
        this.engine = engine;
        this.rudder = rudder;
    }
}

References

https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-aggregation-vs-composition/

uml.png

Cheatsheet