KevsRobots Learning Platform

Create Diagrams with Mermaid

48% complete

Reset my progress

Class Diagrams

Picture your Python classes - their attributes, methods and how they relate - with Mermaid class diagrams

By Kevin McAleer,    5 Minutes


Why draw your classes?

When a robot project grows past one file, you’ll probably start using classes: a Motor class, a Sensor class, a Robot class that uses them. A class diagram shows what each class contains and how the classes fit together, which is a great way to plan code before you write it, or to explain it to someone afterwards.

If classes are new to you, the Python object model course is a good companion to this lesson.


A single class

classDiagram
    class Motor {
        +int pin_a
        +int pin_b
        +int speed
        +forward(speed)
        +backward(speed)
        +stop()
    }

Here’s what it draws:

classDiagram
    class Motor {
        +int pin_a
        +int pin_b
        +int speed
        +forward(speed)
        +backward(speed)
        +stop()
    }

A class box has three parts: the name at the top, the attributes (data) in the middle, and the methods (functions) at the bottom. Mermaid knows which is which because methods have brackets ().

The symbol at the start of each line is its visibility:

Symbol Meaning Python equivalent
+ Public speed
- Private __speed
# Protected _speed

You can show a method’s return type after the brackets, like +read() float.


Relationships

The real power of a class diagram is showing how classes connect. Here are the most useful links:

classDiagram
    Sensor <|-- DistanceSensor
    Robot *-- Motor
    Robot o-- Sensor
    Robot --> Display
    Robot ..> Logger

Here’s what it draws:

classDiagram
    Sensor <|-- DistanceSensor
    Robot *-- Motor
    Robot o-- Sensor
    Robot --> Display
    Robot ..> Logger
  • <|-- Inheritance: DistanceSensor is a Sensor, like class DistanceSensor(Sensor) in Python
  • *-- Composition: Robot owns its motors; they’re created with it and don’t exist without it
  • o-- Aggregation: Robot has sensors, but they could exist on their own and be swapped
  • --> Association: Robot uses a Display
  • ..> Dependency: Robot depends on Logger briefly, such as calling it in one method

Don’t worry too much about the difference between composition and aggregation as a beginner. Inheritance and “has a” are the two you’ll use most.


Add cardinality (how many) in quotes at each end, and a label after a colon:

classDiagram
    class Robot {
        +str name
        +drive(speed)
        +avoid_obstacles()
    }
    class Motor {
        +int speed
        +forward(speed)
        +stop()
    }
    class Sensor {
        <<abstract>>
        +read() float
    }
    class DistanceSensor {
        +int trigger_pin
        +int echo_pin
        +read() float
    }
    class LightSensor {
        +int adc_pin
        +read() float
    }
    Robot "1" *-- "2" Motor : drives
    Robot "1" o-- "*" Sensor : reads
    Sensor <|-- DistanceSensor
    Sensor <|-- LightSensor

Here’s what it draws:

classDiagram
    class Robot {
        +str name
        +drive(speed)
        +avoid_obstacles()
    }
    class Motor {
        +int speed
        +forward(speed)
        +stop()
    }
    class Sensor {
        <<abstract>>
        +read() float
    }
    class DistanceSensor {
        +int trigger_pin
        +int echo_pin
        +read() float
    }
    class LightSensor {
        +int adc_pin
        +read() float
    }
    Robot "1" *-- "2" Motor : drives
    Robot "1" o-- "*" Sensor : reads
    Sensor <|-- DistanceSensor
    Sensor <|-- LightSensor

That reads as: “One robot drives two motors and reads any number of sensors. Distance sensors and light sensors are both kinds of sensor.” <<abstract>> is an annotation: a label that says Sensor is a general idea that you’d never create directly.


From diagram to code

The diagram maps neatly onto Python:

class Sensor:
    def read(self) -> float:
        raise NotImplementedError

class DistanceSensor(Sensor):          # Sensor <|-- DistanceSensor
    def __init__(self, trigger_pin, echo_pin):
        self.trigger_pin = trigger_pin
        self.echo_pin = echo_pin

class Robot:
    def __init__(self, name, sensors):
        self.name = name
        self.motors = [Motor(2, 3), Motor(4, 5)]   # "1" *-- "2" Motor
        self.sensors = sensors                     # "1" o-- "*" Sensor

Try it yourself

  1. Draw a class diagram for an LED class with pin, on(), off() and toggle().
  2. Add an RGBLED class that inherits from LED and adds set_colour(r, g, b).
  3. Challenge: pick a Python project you’ve written with two or more classes and draw it. Did drawing it make you spot anything you’d change?

Common issues

  • Problem: An attribute shows up in the methods section, or the other way round
  • Solution: Make sure methods have () and attributes don’t
  • Why: Mermaid decides which section a line belongs in by looking for brackets

  • Problem: The inheritance arrow points the wrong way
  • Solution: Put the parent on the arrowhead side: Parent <|-- Child
  • Why: The hollow triangle always points at the parent class

  • Problem: A class with spaces in its name breaks the diagram
  • Solution: Use a name without spaces, like DistanceSensor
  • Why: Class names follow the same rules as Python class names

Check your understanding

Quick quiz 1 of 3

How does Mermaid know a line in a class is a method?

Quick quiz 2 of 3

What does Sensor <|-- DistanceSensor mean?

Quick quiz 3 of 3

Which link shows that a robot owns its motors and they don’t exist without it?


< Previous Next >

You can use the arrows  ← → on your keyboard to navigate between lessons.


What are you looking for?
Watch Videos Get Ideas Learn Something Read a Review Read the Blog Search
... Z Z z