UML Class Diagram

Sun, Oct 1, 2023 4-minute read

Class diagrams are part of UML (Unified Modeling Language), which is a standardized graphical representation for specifying, visualizing, constructing, and documenting the artifacts of software systems. In class diagrams, various types of arrows are used to depict relationships between classes.

It’s kind of boring, business-like stuff, but it’s important to know the basics of UML class diagrams. They are used in many places, including design documents, code reviews, and technical interviews. Any complex system should probably be documented with UML diagrams, but for small projects, it’s usually enough to have a basic understanding of class diagrams.

They’re really important for understanding and representing design patterns and object-oriented concepts like inheritance, polymorphism, and encapsulation.

I always find the arrows and relationships in class diagrams hard to remember so here’s a quick refresher.

The most commonly used relationships and arrows:

1. Association (—)

  • Description: Association represents a “using” relationship between two or more classes. It establishes a connection between the classes.
  • Java Example: In the following code, the Person class uses the Address class. This is an example of a simple association.
public class Address {
    String street;
    String city;
    // methods...
}

public class Person {
    Address address;
    // methods...
}

2. Aggregation (◇—)

  • Description: Aggregation is a specialized form of association, representing a “whole-part” or “a-part-of” relationship. It indicates that one class contains another class.
  • Java Example: A Library class contains many Book instances.
import java.util.List;

public class Book {
    String title;
    // methods...
}

public class Library {
    List<Book> books;
    // methods...
}

3. Composition (◆—)

  • Description: Composition is a stronger form of aggregation. It indicates not only a “whole-part” relationship but also specifies that the part cannot exist without the whole.
  • Java Example: In a Computer class, the CPU and Memory components can’t exist without the Computer.
public class CPU {
    // methods...
}

public class Memory {
    // methods...
}

public class Computer {
    CPU cpu = new CPU();
    Memory memory = new Memory();
    // methods...
}

4. Inheritance (▷—)

  • Description: Inheritance depicts an “is-a” relationship between the base class (parent) and the derived class (child).
  • Java Example: A Vehicle class can be a parent class, and Car and Bike classes can be its children.
public class Vehicle {
    // methods...
}

public class Car extends Vehicle {
    // methods...
}

public class Bike extends Vehicle {
    // methods...
}

5. Dependency (←——)

  • Description: Dependency represents a “uses” relationship between two classes where one class depends on another temporarily.
  • Java Example: A Driver class depends on the License class to perform the drive() operation.
public class License {
    // methods...
}

public class Driver {
    public void drive(License license) {
        // methods...
    }
}

6. Realization (⇒)

  • Description: Realization represents a relationship where a class implements an interface.
  • Java Example: A Bird class can implement the Flyable interface.
public interface Flyable {
    void fly();
}

public class Bird implements Flyable {
    public void fly() {
        // implementation
    }
}

In class diagrams, these arrows help to quickly understand the relationships and dependencies between classes, making it easier to read and maintain the code.

House Example

Here’s a simple example of a class diagram showing the main relationships between classes:

The Main Relationships in a Class Diagram

// Association: A House has a Door (but a Door can exist independently)
class Door {
    // ...
}

// Aggregation: A House has multiple Windows (but Windows can exist independently)
class Window {
    // ...
}

// Composition: A House has multiple Rooms (and Rooms cannot exist without a House)
class Room {
    // ...
}

// Inheritance: An Apartment is a type of House
class Apartment extends House {
    // ...
}

// Dependency: House uses Electricity (but they are not permanently tied)
class Electricity {
    // ...
}

// Realization: House must adhere to the BuildingCode interface
interface BuildingCode {
    void adhereToCode();
}

class House implements BuildingCode {
    Door door;
    List<Window> windows;
    List<Room> rooms;

    @Override
    public void adhereToCode() {
        // implementation
    }

    // Dependency example
    public void consumeElectricity(Electricity electricity) {
        // ...
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        // Composition
        House house = new House();
        Room room1 = new Room();
        Room room2 = new Room();
        house.rooms = Arrays.asList(room1, room2);

        // Aggregation
        Window window1 = new Window();
        Window window2 = new Window();
        house.windows = Arrays.asList(window1, window2);

        // Association
        Door door = new Door();
        house.door = door;

        // Dependency
        Electricity electricity = new Electricity();
        house.consumeElectricity(electricity);

        // Inheritance and Realization are evident from the class definitions
    }
}