Skip to content

UML Lesson

Class diagrams

Learning Objectives

  1. Know how to draw class diagrams and how to describe classes and their attributes, constructors, and methods
  2. Know how to describe connections between classes and describe inheritance and interface implementation
  3. Can implement a class based on a class diagram

A class diagram is a diagram used in designing and modeling software to describe classes and their relationships. Class diagrams enable us to model software in a high level of abstraction and without having to look at the source code.

Classes in a class diagram correspond with classes in the source code. The diagram shows the names and attributes of the classes, connections between the classes, and sometimes also the methods of the classes.

Next we will get familiar with creating and reading class diagrams using UML. Using a unified modeling language ensures that class diagrams drawn by different people can be read and understood by everyone familiar with ANY OOP language.

Describing class and class attributes

  • First we will describe one class and its attributes.
  • Below is the source code for a class called Person which has two class attributes name and age.
1
2
3
4
public class Person {
    public String name;
    private int age;
}
  • In a class diagram, a class is represented by a rectangle with the name of the class written on top.
  • A line below the name of the class divides the name from the list of attributes (names and types of the class variables).
  • The attributes are written one attribute per line.
  • In some class diagrams, class attributes are written “attributeName: attributeType”, but in the mermaid diagrams we’ll be using, it sticks to attribute type attribute name.
  • A + before the attribute name means the attribute is public.
  • A - means the attribute is private.
    • Speak up now if you don’t recognize public/private access modifiers!
classDiagram class Person{ +String name -int age }

Describing class constructor

Below we have the source code for a constructor for our Person class. The constructor gets the name of the person as a parameter.

1
2
3
4
5
6
7
8
9
public class Person {
    private String name;
    private int age;

    public Person(String initialName) {
        this.name = initialName;
        this.age = 0;
    }
}
  • In a class diagram, we list the constructor (and all other methods) below the attributes.
  • A line below the attributes list separates it from the method list.
  • Methods are written with +/- (depending on the visibility of the method), method name, types and their parameters.
  • The constructor above is written +Person(String initialName)
  • The parameters are written the same way class attributes are — “parameterType parameterName”.
classDiagram class Person{ -String name -int age +Person(String initialName) }

Describing class methods

Below we have added a method printPerson() which returns void to the Person class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Person {
    private String name;
    private int age;

    public Person(String initialName) {
        this.name = initialName;
        this.age = 0;
    }

    public void printPerson() {
        System.out.println(this.name + ", age " +   this.age + " years");
    }
}
  • In a class diagram, we list all class methods including the constructors; constructors are listed first and then all class methods.
  • We also write the return type of a method in the class diagram.
classDiagram class Person{ -String name -int age +Person(String initialName) +printPerson() void }
Info

A class diagram describes classes, constructors and methods

A class diagram describes classes and their attributes, constructors and methods as well as the connections between classes. However a class diagram tells us nothing about the implementation of the constructors or the methods. Therefore a class diagram describes the structure of an object but NOT its functionality.

For example the method printPerson uses the class attributes name and age, but this cannot be seen from the class diagram.

Below we have added the method getName to the Person class which returns the name of the Person.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Person {
    private String name;
    private int age;

    public Person(String initialName) {
        this.name = initialName;
        this.age = 0;
    }

    public void printPerson() {
        System.out.println(this.name + ", age " +   this.age + " years");
    }

    public String getName() {
        return this.name;
    }
}
classDiagram class Person{ -String name -int age +Person(String initialName) +printPerson() void +getName() String }

Section Exercise 1:

The class diagram below shows the class Customer. Implement the class in the exercise.

classDiagram class Customer{ -String name -String address -String email }
Answer
1
2
3
4
5
public class Customer {
    private String name;
    private String address;
    private String email;
}

Section Exercise 2:

The class diagram below depicts the classes Book and Plane. Implement the classes in the exercise.

classDiagram class Book{ -String name -String author -int pageCount } class Plane{ -String ID -String model -int yearOfIntroduction }
Answer
1
2
3
4
5
public class Book {
    private String name;
    private String author;
    private int pageCount;
}
1
2
3
4
5
public class Plane {
    private String ID;
    private String model;
    private int yearOfIntroduction;
}

Connections between classes

In a class diagram, the connections between classes are shown as arrows. The arrows also show the direction of the connection.

Below we have a class Book.

1
2
3
4
5
6
public class Book {
    private String name;
    private String publisher;

    // constructors and methods
}
classDiagram class Book{ -String name -String author -String publisher -int pageCount }

If we add a variable author, type of which is Person, in the source code the variable is declared like all other class variables.

1
2
3
4
5
6
7
public class Book {
    private String name;
    private String publisher;
    private Person author;  // This is the new variable

    // constructors and methods
}
  • In a class diagram variables which refer to other objects are not written with the rest of the class attributes.
  • They are shown as connections between the classes.

In the class diagram below we have the classes Person and Book, and the connection between them.

classDiagram class Book{ -String name -String author -String publisher -int pageCount } class Person{ -String name -int age +Person(String initialName) +printPerson() void +getName() String } Book --> Person: author
  • The arrow shows the direction of the connection.
  • The connection above shows that a Book knows its author but a Person does not know about books they are the author of.
  • We can also add a label to the arrow to describe the connection.
    • In the above diagram the arrow has an accompanying label telling us that a Book has an author.

If a book has multiple authors, the authors are saved to a list.

1
2
3
4
5
6
7
public class Book {
    private String name;
    private String publisher;
    private ArrayList<Person> authors;

    // constructors and methods
}
  • In a class diagram, this situation is described by adding a star to the end of the arrow showing the connection between the classes.
  • The star tells us that a book can have between 0 and unlimited number of authors.
  • Below we have not amended the label to describe the cardinality / multiplicity of the connection.
classDiagram class Book{ -String name -String author -String publisher -int pageCount } class Person{ -String name -int age +Person(String initialName) +printPerson() void +getName() String } Book "0" --> "*" Person: authors
  • Class methods are described just like we did before.
  • Below we have added methods getAuthors and addAuthor to the Book class.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Book {
    private String name;
    private String publisher;
    private ArrayList<Person> authors;

    // constructor

    public ArrayList<Person> getAuthors() {
        return this.authors;
    }

    public void addAuthor(Person author) {
        this.authors.add(author);
    }
}
classDiagram class Book{ -String name -String author -String publisher -int pageCounta +getAuthors() ArrayList +addAuthor(Person author) } class Person{ -String name -int age +Person(String initialName) +printPerson() void +getName() String } Book "0" --> "*" Person: authors

Section exercise 1:

In the class diagram below, the classes Show and Ticket and their connection are depicted. The star is at the Ticket end of the connection - in this case the star gives some extra information of the connection; even though a show doesn’t know about the tickets that have been sold to it, you can still sell many tickets to one show.

Implement the classes in the diagram in the exercise base.

classDiagram class Show{ -String movie -String time } class Ticket{ -int seat -int code } Ticket "*" --> "1" Show: sell
Answer
1
2
3
4
public class Show {
    private String movie;
    private String time;
}
1
2
3
4
5
public class Ticket {
    private int seat;
    private int code;
    private Show show;
}
  • If there is no arrowhead in a connection, both classes know about each other.
  • Below is an example where a book knows about its author and a person knows about a book they have written.
classDiagram class Book{ -String name -String author -String publisher -int pageCounta +getAuthors() ArrayList +addAuthor(Person author) } class Person{ -String name -int age +Person(String initialName) +printPerson() void +getName() String } Book "1" -- "*" Person: authors
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Person {
    private String name;
    private int age;
    private Book book;

    // ...
}
public class Book {
    private String name;
    private String publisher;
    private ArrayList<Person> authors;

    // ...
}
  • By default — if there is no star on the connection — the connection is singular.
  • The classes above are interesting, because a Person can only have one book.

  • If a person can have multiple books and a book can have multiple authors, we add a star to both ends of the connection:

classDiagram class Book{ -String name -String author -String publisher -int pageCounta +getAuthors() ArrayList +addAuthor(Person author) } class Person{ -String name -int age +Person(String initialName) +printPerson() void +getName() String +getBooks() ArrayList } Book "*" -- "*" Person: authors
  • Now the person class would be as follows:
1
2
3
4
5
6
7
8
9
import java.util.ArrayList;

public class Person {
    private String name;
    private int age;
    private ArrayList<Book> books;

    // ...
}

Section Exercise 2:

Two classes, Student and University, are depicted below, as well as the connection between them. Implement these classes in the exercise base.

classDiagram class Student{ -int studentID -String name } class University{ -String name } Student "*" -- University: attends
Answer
1
2
3
4
5
public class Student {
    private int studentID;
    private String name;
    private University university;
}
1
2
3
4
public class University {
    private String name;
    private ArrayList<Student> students;
}

Describing inheritance

  • In a class diagram inheritance is described by an arrow with a triangle head.
  • The triangle points to the class being inherited from.

In the below example the Engine inherits the class Part.

classDiagram class Part{ -String id -String manufacturer -String description } class Engine{ -String type } Part <|-- Engine

In the below example the ProductWarehouseWithHistory class inherits the ProductWarehouse class, which, in turn, inherits the Warehouse class. ChangeHistory is a separate class connected to the ProductWarehouse. ProductWarehouseWithHistory knows about the ChangeHistory but the ChangeHistory does not know about the ProductWarehouseWithHistory.

classDiagram class Warehouse{ -double capacity -double balance +Warehouse(double capacity) +getBalance() double +getCapacity() double +howMuchSpaceLeft() double +addToWarehouse(double amount) void +takeFromWarehouse(double amount) double +toString() toString } class ProductWarehouse{ -String name +ProductWarehouse(String name, double capacity) +getName() String +setName(String name) String +toString() String } class ProductWarehouseWithHistory{ +ProductWarehouseWithHistory(String name, double capacity, double initialBalance) +history() String +printAnalysis() void +addToWarehouse(double amount) void +takeFromWarehouse(double amount) double } class ChangeHistory{ -ArrayList~String~ states +ChangeHistory() +add(double status) void +clear() void } Warehouse <|-- ProductWarehouse ProductWarehouse <|-- ProductWarehouseWithHistory ProductWarehouse --> ChangeHistory
  • Inheritance of abstract classes is described almost the same way as regular classes. However, we add the description <<abstract>> above the name of the class.
  • The name of the class and its abstract methods are also written in itallics.
classDiagram class Box{ <<abstract>> +add(Item item) void* +add(Collection items) void +isInBox(Item item) boolean* } class BoxWithMaximumWeight{ -int maximumWeight +BoxWithMaximumWeight(int capacity) +add(Item item) void +isInBox(Item item) boolean } class Item{ +String name +int weight +Item(String name, int weight) +Item(String name) +getWeight() int } Box <|-- BoxWithMaximumWeight BoxWithMaximumWeight --> Item

Section Exercise 1:

The classes Player and Bot and the connection between them are depicted in the class diagram below. Implement these classes in the exercise.

classDiagram class Player{ -String name +play() void +printName() void } class Bot{ +play() void +addMove(String move) void } Player <|-- Bot
Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class Player {
    private String name;

    public void play() {
        // Real person makes move
    }

    public void printName() {
        System.out.println(this.name);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import java.util.List;
import java.util.ArrayList;

public class Bot {
    private List<String> moves;

    public Bot() {
        this.moves = new ArrayList<>();
    }

    public void play() {
        // Real person makes move
    }

    public void addMove(String move) {
        this.moves.add(move);
    }
}

Describing interfaces

  • In class diagrams, interfaces are written <<interface>> NameOfTheInterface.
  • Below we describe an interface Readable.
1
2
3
public interface Readable {

}
classDiagram class Readable{ <<interface>> }
  • Methods are described just like they are for a class.
  • Implementing an interface is shown as a dashed arrow with a triangle arrowhead.
  • Below, we describe a situation where Book implements interface Readable.
classDiagram class Readable{ <<interface>> +read() void* } class Book { +read() void* } Readable <|.. Book: implements

Section exercise 1:

Below you’ll see the interface Saveable and the class Person. Implement the contents of this class diagram in the exercise base.

classDiagram class Saveable{ <<interface>> +save() void +delete() void +load(String address) void } class Person { -String name -String address } Saveable <|.. Person: implements
Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface Saveable {
    void save();
    void delete();
    void load(String address);
}
public class Person implements Saveable {
    private String name;
    private String address;

    public Person(String name, String address) {
        this.name = name;
        this.address = address;
    }

    @Override
    public void save() {}

    @Override
    public void delete() {}

    @Override
    public void load(String address){}
}

How these should be drawn?

Class diagrams are an excellent way to describe a problem and a problem-domain to others. They are particularily useful when a programmer is designing a program with multiple classes.

Often a class diagram is drawn on a whiteboard or a large sheet of paper during the design phase. Class diagrams should be thought of as helpful tools to build a program, which can be thrown away afterwards. You should not use too much energy to think about the correctness and details of the modeling language. Class diagrams should also be drawn in a suitable level of abstraction. For example, if you have tens of classes, it might not be worth describing each attribute and each method of each class; getting a good overview of the program structure is the most important.

The class diagrams here have been drawn using mermaid.live. Some IDEs also have tools for drawing class diagrams; for example, easyUML draws class diagrams from the source code, IntelliJ has a mermaid plugin, and of course all of the AI Bots can be prompted to draw these diagrams.

Section exercise 1:

Below you’ll see a somewhat larger class diagram. In it are the classes A, B, C, D, and E, as well as the interfaces IA, IB, and IC. Create these classes and interfaces using ChatGPT.

classDiagram class IA { <<interface>> } class IB { <<interface>> } class IC { <<interface>> } class A { } class B { } class C { } class D { } class E { } A <|-- B : Inheritance "is_a" B <|-- C : Inheritance "is_a" C "*" -- "*" E : Association "has_a" D "1" --> "1" IA : Association "has_a" IA <|.. A : implements "is_a" IB <|.. B : implements "is_a" IC <|.. C : implements "is_a"
Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Interfaces
public interface IA {
    // define behaviors for IA
}

public interface IB {
    // define behaviors for IB
}

public interface IC {
    // define behaviors for IC
}

// Classes
public class A implements IA {
    // Fields, constructors, methods
}

public class B extends A implements IB {
    // Fields, constructors, methods
}

public class C extends B implements IC {
    // Association: C has many E
    private java.util.List<E> eList = new java.util.ArrayList<>();

    // Fields, constructors, methods
    public void addE(E e) {
        eList.add(e);
    }

    public java.util.List<E> getEs() {
        return eList;
    }
}

public class D {
    // Association: D has one IA
    private IA ia;

    public D(IA ia) {
        this.ia = ia;
    }

    public IA getIA() {
        return ia;
    }

    public void setIA(IA ia) {
        this.ia = ia;
    }
}

public class E {
    // Fields, constructors, methods
}

Section Exercise 2:

Use the service on the website mermaid.live to create a class diagram that represents the following classes. Check your textual representation used to create the diagram from the service to the answer below

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Teacher{
    private String name;
    public Teacher(String name) {
        this.name = name;
    }
    public String toString() {
        return this.name;
    }
}
public class Class{
    private Teacher teacher;
    private String course;

    public Class(Teacher teacher, String course) {
        this.teacher = teacher;
        this.course= course;
    }
    public void printInformation() {
        System.out.println(this.course + ", teacher: " + this.teacher);
    }
}
import java.util.ArrayList;

public class LearningInstitution {
    private ArrayList<Teacher> teachers;
    public LearningInstitution() {
        this.teachers = new ArrayList<>();
    }
    public void addTeacher(Teacher teacher) {
        this.teachers.add(teacher);
    }
    public String toString() {
        return "Number of teachers in the learning institution: " + this.teachers.size();
    }
}
Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
classDiagram
    class Teacher {
        - String name
        + Teacher(String name)
        + toString() String
    }

    class Class {
        - Teacher teacher
        - String course
        + Class(Teacher teacher, String course)
        + printInformation() void
    }

    class LearningInstitution {
        - ArrayList<Teacher> teachers
        + LearningInstitution()
        + addTeacher(Teacher teacher) void
        + toString() String
    }

    Class "*" --> "1" Teacher : has-a
    LearningInstitution "1" --> "*" Teacher : contains
Composition VS Association

Finale Note:Association is a broad term for any link between two objects, while composition is a strong, exclusive part-of relationship where the contained object’s lifecycle is tied to the containing object. In association, both objects can exist independently, but in composition, the contained object cannot exist without its parent.