Detailed Table of Contents
Guidance for the item(s) below:
You are strongly recommended to watch pre-recorded videos of this week (x1.25 or x1.5 speed is recommended) as they have more elaborate explanations and visualizations. Read the text after watching the video.
This week, we start learning a UML diagram called Class Diagrams. As we learn about class diagrams, we should also learn about object diagrams as they complement each other.
Can explain/identify class diagrams
UML class diagrams describe the structure (but not the behavior) of an OOP solution. These are possibly the most often used diagrams in the industry and are an indispensable tool for an OO programmer.
An example class diagram:
Can draw UML classes
The basic UML notations used to represent a class:
A Table
class shown in UML notation:
The equivalent code
The 'Operations' compartment and/or the 'Attributes' compartment may be omitted if such details are not important for the task at hand. Similarly, some attributes/operations can be omitted if not relevant. 'Attributes' always appear above the 'Operations' compartment. All operations should be in one compartment rather than each operation in a separate compartment. Same goes for attributes.
The visibility of attributes and operations is used to indicate the level of access allowed for each attribute or operation. The types of visibility and their exact meanings depend on the programming language used. Here are some common visibilities and how they are indicated in a class diagram:
+
: public-
: private#
: protected~
: package privateHow visibilities map to programming language features
Table
class with visibilities shown:
The equivalent code
Generic classes can be shown as given below. The notation format is shown on the left, followed by two examples.
Exercises
Can explain class-level members
While all objects of a class have the same attributes, each object has its own copy of the attribute value.
All Person
objects have the name
attribute but the value of that attribute varies between Person
objects.
However, some attributes are not suitable to be maintained by individual objects. Instead, they should be maintained centrally, shared by all objects of the class. They are like ‘global variables’ but attached to a specific class. Such variables whose value is shared by all instances of a class are called class-level attributes.
The attribute totalPersons
should be maintained centrally and shared by all Person
objects rather than copied at each Person
object.
Similarly, when a normal method is being called, a message is being sent to the receiving object and the result may depend on the receiving object.
Sending the getName()
message to the Adam
object results in the response "Adam"
while sending the same message to the Beth
object results in the response "Beth"
.
However, there can be methods related to a specific class but not suitable for sending messages to a specific object of that class. Such methods that are called using the class instead of a specific instance are called class-level methods.
The method getTotalPersons()
is not suitable to send to a specific Person
object because a specific object of the Person
class should not have to know about the total number of Person
objects.
Class-level attributes and methods are collectively called class-level members (also called static members sometimes because some programming languages use the keyword static
to identify class-level members). They are to be accessed using the class name rather than an instance of the class.
Exercises
Can interpret class-level members in class diagrams
In UML class diagrams, underlines denote class-level attributes and methods.
In the class diagram below, the totalStudents
attribute and the getTotalStudents
method are class-level.
Can explain associations
Objects in an OO solution need to be connected to each other to form a network so that they can interact with each other. Such connections between objects are called associations.
Suppose an OOP program for managing a learning management system creates an object structure to represent the related objects. In that object structure you can expect to have associations between a Course
object that represents a specific course and Student
objects that represent students taking that course.
Associations in an object structure can change over time.
To continue the previous example, the associations between a Course
object and Student
objects can change as students enroll in the module or drop the module over time.
Associations among objects can be generalized as associations between the corresponding classes too.
In our example, as some Course
objects can have associations with some Student
objects, you can view it as an association between the Course
class and the Student
class.
You use instance level variables to implement associations.
Can explain/use association labels in class diagrams
Association labels describe the meaning of the association. The arrow head indicates the direction in which the label is to be read.
In this example, the same association is described using two different labels.
Admin
class is associated with Student
class because an Admin
object uses a Student
object.Admin
class is associated with Student
class because a Student
object is used by an Admin
object.Can explain/use association roles in class diagrams
Association Role labels are used to indicate the role played by the classes in the association.
This association represents a marriage between a Man
object and a Woman
object. The respective roles played by objects of these two classes are husband
and wife
.
Note how the variable names match closely with the association roles.
class Man {
Woman wife;
}
class Woman {
Man husband;
}
class Man:
def __init__(self):
self.wife = None # a Woman object
class Woman:
def __init__(self):
self.husband = None # a Man object
The role of Student
objects in this association is charges
(i.e. Admin is in charge of students)
class Admin {
List<Student> charges;
}
class Admin:
def __init__(self):
self.charges = [] # list of Student objects
Can explain the meaning of multiplicity
Multiplicity is the aspect of an OOP solution that dictates how many objects take part in each association.
The multiplicity of the association between Course
objects and Student
objects tells you how many Course
objects can be associated with one Student
object and vice versa.
A normal instance-level variable gives us a 0..1
multiplicity (also called optional associations) because a variable can hold a reference to a single object or null
.
In the code below, the Logic
class has a variable that can hold 0..1
i.e., zero or one Minefield
objects.
class Logic {
Minefield minefield;
// ...
}
class Minefield {
//...
}
class Logic:
def __init__(self):
self.minefield = None
# ...
class Minefield:
# ...
A variable can be used to implement a 1
multiplicity too (also called compulsory associations).
In the code below, the Logic
class will always have a ConfigGenerator
object, provided the variable is not set to null
at some point.
class Logic {
ConfigGenerator cg = new ConfigGenerator();
...
}
class Logic:
def __init__(self):
self.config_gen = ConfigGenerator()
Bidirectional associations require matching variables in both classes.
In the code below, the Foo
class has a variable to hold a Bar
object and vice versa i.e., each object can have an association with an object of the other type.
class Foo {
Bar bar;
//...
}
class Bar {
Foo foo;
//...
}
class Foo:
def __init__(self, bar):
self.bar = bar;
class Bar:
def __init__(self, foo):
self.foo = foo;
To implement other multiplicities, choose a suitable data structure such as Arrays, ArrayLists, HashMaps, Sets, etc.
This code uses a two-dimensional array to implement a 1-to-many association from the Minefield
to Cell
.
class Minefield {
Cell[][] cell;
...
}
class Minefield:
def __init__(self):
self.cells = {1:[], 2:[], 3:[]}
Can explain what is the multiplicity of an association
Commonly used multiplicities:
0..1
: optional, can be linked to 0 or 1 objects.1
: compulsory, must be linked to one object at all times.*
: can be linked to 0 or more objects.n..m
: the number of linked objects must be within n
to m
inclusive. In the diagram below, an Admin
object administers (is in charge of) any number of students but a Student
object must always be under the charge of exactly one Admin
object.
In the diagram below,
Exercises
Can explain the meaning of navigability
When two classes are linked by an association, it does not necessarily mean the two objects taking part in an instance of the association knows about (i.e., has a reference to) each other. The concept of which object in the association knows about the other object is called navigability.
Navigability can be unidirectional or bidirectional. Suppose there is an association between the classes Box
and Rope
, and the Box
object b
and the Rope
object r
is taking part in one instance of that association.
Box
to Rope
, b
will have a reference to r
but r
will not have a reference to b
. Similarly, if the navigability is in the other direction, r
will have a reference to b
but b
will not have a reference to r
.b
will have a reference to r
and r
will have a reference to b
i.e., the two objects will be pointing to each other for the same single instance of the association.Note that two unidirectional associations in opposite directions do not add up to a single bidirectional association.
In the code below, there is a bidirectional association between the Person
class and the Cat
class i.e., if Person
p
is the owner of the Cat
c
, p
it will result in p
and c
having references to each other.
class Person {
Cat pet;
//...
}
class Cat{
Person owner;
//...
}
class Person:
def __init__(self):
self.pet = None # a Cat object
class Cat:
def __init__(self):
self.owner = None # a Person object
The code below has two unidirectional associations between the Person
class and the Cat
class (in opposite directions) because the breeder is not necessarily the same person keeping the cat as a pet i.e., there are two separate associations here, which rules out it being a bidirectional association.
class Person {
Cat pet;
//...
}
class Cat{
Person breeder;
//...
}
class Person:
def __init__(self):
self.pet = None # a Cat object
class Cat:
def __init__(self):
self.breeder = None # a Person object
Can interpret association navigabilities in class diagrams
Use arrowheads to indicate the navigability of an association.
In this example, the navigability is unidirectional, and is from the Logic
class to the Minefield
class. That means if a Logic
object L
is associated with a Minefield
object M
, L
has a reference to M
but M
doesn't have a reference to L
.
class Logic {
Minefield minefield;
// ...
}
class Minefield {
//...
}
class Logic:
def __init__(self):
self.minefield = None
# ...
class Minefield:
# ...
Here is an example of a bidirectional navigability; i.e., if a Dog
object d
is associated with a Man
object m
, d
has a reference to m
and m
has a reference to d
.
class Dog {
Man man;
// ...
}
class Man {
Dog dog;
// ...
}
class Dog:
def __init__(self):
self.man = None
# ...
class Man:
def __init__(self):
self.dog = None
# ...
Navigability can be shown in class diagrams as well as object diagrams.
According to this object diagram, the given Logic
object is associated with and aware of two MineField
objects.
Exercises
Can show an association as an attribute
An association can be shown as an attribute instead of a line.
Association multiplicities and the default value can be shown as part of the attribute using the following notation. Both are optional.
name: type [multiplicity] = default value
The diagram below depicts a multi-player Square Game being played on a board comprising of 100 squares. Each of the squares may be occupied with any number of pieces, each belonging to a certain player.
A Piece
may or may not be on a Square
. Note how that association can be replaced by an isOn
attribute of the Piece
class. The isOn
attribute can either be null
or hold a reference to a Square
object, matching the 0..1
multiplicity of the association it replaces. The default value is null
.
The association that a Board
has 100 Square
s can be shown in either of these two ways:
Show each association as either an attribute or a line but not both. A line is preferred as it is easier to spot.
Can draw UML objects
Notation:
Notes:
car1:Car
are underlined.objectName:ClassName
is meant to say 'an instance of ClassName
identified as objectName
'.:Car
which is meant to say 'an unnamed instance of a Car object'.Some example objects:
Exercises
Can distinguish between class diagrams and object diagrams
Compared to the notation for class diagrams, object diagrams differ in the following ways:
:
before the class nameFurthermore, multiple object diagrams can correspond to a single class diagram.
Both object diagrams are derived from the same class diagram shown earlier. In other words, each of these object diagrams shows ‘an instance of’ the same class diagram.
When the class diagram has an inheritance relationship, the object diagram should show either an object of the parent class or the child class, but not both.
Suppose Employee
is a child class of the Person
class. The class diagram will be as follows:
Now, how do you show an Employee
object named jake
?
This is not correct, as there should be only one object.
This is OK.
This is OK, as
jake
is a Person
too.
That is, we can show the parent class instead of the child class if the child class doesn't matter to the purpose of the diagram (i.e., the reader of this diagram will not need to know that jake
is in fact an Employee
).
Association labels/roles can be omitted unless they add value (e.g., showing them is useful if there are multiple associations between the two classes in concern -- otherwise you wouldn't know which association the object diagram is showing)
Consider this class diagram and the object diagram:
We can clearly see that both Adam and Eve lives in hall h1 (i.e., OK to omit the association label lives in
) but we can't see if History is Adam's major or his minor (i.e., the diagram should have included either an association label or a role there). In contrast, we can see Eve is an English major.
Exercises
Follow up notes for the item(s) above:
After learning the UML notations above, do the following in the given order: