Just having diagrams for each class in a system independently is very useful. However, UML diagrams are more useful when we also depict the relationships between the classes. This allows us to tell at a glance which classes depend on other ones, and which classes contain instances of other ones.
There are multiple types of relationships between classes that can be included
in UML diagrams. The simplest is the dependency relationship.
This just indicates that one class uses another in some way. For example, if
we are building a game of some kind, we might have a Game
class
which creates and uses a Player
object. That could be signaled
with a dependency relationship which looks like this:
Here we are leaving off the variables and methods of the classes in question to make the diagram simpler.
This is the weakest form of relationship between classes. It just indicates that a given class uses another to implement one or more of its methods. In this example, maybe we have the Game class contain a method which makes a Player.
We can add a stereotype to the dependency arrow that indicates what sort of dependency is represented. This is commonly done with the terms "uses" or "instantiates". The latter means the class creates instances of the other, while the former means we just use the class in some way.
The next stronger type of relationship is the has-a relationship
which indicates that one class contains an instance of another. For example, in our
game we might have the Player
class contain an instance variable of
type Map
. That's done with a solid arrow:
This is a stronger relationship than a dependency because we don't just use one class to implement another, but store an instance inside of the other class. Here, the Player will store a Map object as an instance variable (as opposed to just creating one inside of a method body).
When including a "has-a" relationship, we would not also include the object in the variables section of the class which has the other, because it would be redundant. This means that Player would not list a Map object as one of its instance variables.
The next strongest type of relationship is called aggregation. This is like the "has-a" relationship except it indicates that one class is the sole owner of objects of another class. In the example with the Player and the Map, it might be the case that the Player has a reference to a Map object, but lots of other objects do too. Maybe all the enemy player objects also contain references to the same Map. In that case it would not be aggregation, but just "has-a".
Let's say the Player contains an instance of the Inventory
class. In this
case, we might want to indicate that the Player owns that Inventory object completely and
no other objects will have references to it. That's done with the aggregation relationship:
Notice that the arrow goes the opposite way of has-a and dependency arrows. I don't know why, that's just the way it is.
Note that aggregation indicates a relationship between particular objects, not necessarily all the objects in a system. In this case we're saying that a Player object owns an Inventory object. But there might be other Inventory objects not owned by the Player. For instance we could have Enemy objects which also own an Inventory.
The last type of relationship is called composition. It's exactly the same as aggregation, but also says that the lifetime of the composed object doesn't go past the lifetime of the object it's in. This is shown in UML by filling in the arrowhead:
This would indicate that the Inventory object should be destroyed when the Player object it's inside of is destroyed. This distinction doesn't normally matter, especially in Java where the garbage collector takes care of matters like this mostly without us needing to worry about it.
We can also indicate how many of each object another object contains. This is done by putting little numbers next to the ends of arrows in a relationship. For example, let's say we have each Player class contain a list of Item objects. In that case, we should indicate the multiplicity of the relationship:
Here we have the * label next to the Item class. This indicates that there are 0 or more Item objects stored with each Player. We can also list things like "1...10" if we know there will be between 1 and 10 items.
The other side of the relationship has a "1". This tells us that each Item is only stored in one Player object. Multiplicity is not required for relationships, but should be included when it provides helpful information about the system.
Finally we can include roles in a relationship which tell us extra information about what the relationship represents. Sometimes this isn't needed as the relationship role is kind of obvious. For example, we probably can guess that the Item objects the player has are the things they are carrying. It is especially useful when we have multiple relationships between two classes.
For example, let's say we are building a system like Banner. We might have Student and Professor classes. There are multiple types of relationships between Students and Professors. Let's say we want the Student to have references to their current instructors, and also their advisor. And we also want the Professor to have references to their current students and advisees. We could indicate all that with a diagram like this:
Here we have role names for each part of the relationship. We indicate these two relationships between the classes. The first is the advisor-advisee relationship in which each Professor has many advisees, but each Student has one advisor. The other is the instructor-pupil relationship in which Students have multiple instructors and Professors have multiple pupils.
Copyright © 2024 Ian Finlayson | Licensed under a Creative Commons BY-NC-SA 4.0 License.