Do I need to know that?
Yes, but the bare minimum will be just enough. I believe that every software engineer should know what an activity, state, and class diagrams are and know how to read them.
You may ask yourself right now: “Okay, but why do I need to know that?”
For better communication within your team and other teams.
I cannot count how many times I was discussing with other iOS Developers about structure and architecture of code using natural language. We spent hours to finally understand that we had the same idea since the beginning of the conversation.
However, we wasted so much time trying to communicate without any better formal language to model our code.
And then I had to explain the same things to Android guys… (What is a protocol
in your Swift? You mean interface
like in Kotlin? Uggghh…)
Essentials of UML class diagrams: relations
I’m not going to talk about modeling classes itself (properties, methods). I think the most interesting and important thing is to model how classes are coupled, where are the dependencies and what is abstraction layer and what is concrete implementation.
The UML standard classifies following kind of relations:
- association (one data type is a property of another one)
- aggregation (property can live outside of given data type)
- composition (every property of data type is deallocated along with this data type)
- dependency (for example passing as argument)
- realization (implementation of abstractions of some sort - like
interface
in Java/Kotlin orprotocol
in Swift) - generalization (inheritance)
In the case of an association, I’d like to skip the detailed explanation of aggregation and composition in this write-up. It’s important for modeling in general but it may be not that important to communicate your architecture in general within your team (at least during initial phases of design).
Note \ The difference between aggregation and composition is quite simple yet hard for me to explain in plain English. Let’s say that you have data type
A
that has a property of typeB
. If you destroy the instance of objectA
but the instances ofB
are not deallocated - then its aggregation. If your property of typeB
is deallocated along withA
then it’s composition.
Association
This is the easiest one to explain. It is a simple line from one class to another - but since we’re dealing with reference and value types, therefore not everything is a class - let’s say that it’s a line between some rectangles 🙂It models relation “who owns who”
The most important thing here is the arrow. It tells us the direction on who owns what.
In this case it means: Data type Bicycle
has property of type Saddle
Implementation in Swift:
1
2
3
4
5
6
7
struct Saddle {}
struct Bicycle {
let saddle: Saddle // UML diagram didn't specify name for this property
}
let bicycle = Bicycle(saddle: Saddle())
Dependency
This relation is pictured as a dashed line. Like association, it can have arrows, cardinality, or names but the most important thing to remember is that it is the weak relation. It means that one data type can be an argument to method/function/constructor/whatever in another data type but there is no property for that.
It means: Some of my methods needs that data type as their parameter but I don’t need to own any of those data types as my properties.
1
2
3
4
5
6
class CustomView: UIView {
//...
func setBackgroundGradientColors(top: UIColor, bottom: UIColor) {
//...
}
}
CustomView
depends on instances of UIColor
but it doesn’t have to have properties such as top
and bottom
.
Realisation
It tells that something is implementing some sort of interface. Presenting associations and realizations is the key point to understand what are the “moving parts” of application, where to put dependency injection, what should be loosely coupled etc.
1
2
3
4
5
6
7
8
9
10
protocol FullyNamed {
var fullName: String { get }
}
struct Person: Printable {
//...
var fullName: String {
//...
}
}
Generalisation
It’s a classic object-oriented inheritance. I think no extra explanation is needed 🙂
1
2
3
class ViewController: UIViewController {
//...
}
What are those arrows, numbers, and labels?
Arrows describe what particular data type owns. In the case of Bicycle
-Saddle
example, the arrows tell us that instance of type Bicycle
has some sort of property of type Saddle
and not the other way around.
There can be also a line without any arrows. It happens when there is a two-directional connection between two data types.
Numbers say the cardinality which you may already know from modeling persistent storage (CoreData
, Realm
). We can specify answers to the following questions:
- Does it has a single instance of other data type or entire collection?
- Is it allowed having none of them?
Labels give us extra clues on how to name properties on the particular data types. Some certified UML experts may cry while reading this but honestly, it’s just it in practice 🙂
1
2
3
4
5
6
7
8
9
10
11
12
13
struct TextView {
var parent: MainView?
}
struct MainView {
var textInputs: [TextView] = []
}
let mainView = MainView()
let topTextView = TextView(parent: mainView)
let bottomTextView = TextView(parent: mainView)
mainView.textInputs = [topTextView, bottomTextView]
Summary
We went through the most interesting parts of UML’s class diagrams just to get you started to draw your lines, arrows, and rectangles. I used enormous simplification and this is not a fully competent UML guide by any means. I think it’s just enough to understand how to visualize your sketches in code and vice versa.
I hope it will save you some time during the next meeting with other fellow developers!