The Unified Modelling Language (UML) provides a standardized way to visualize and communicate software system design. UML diagrams can be either structural or behavioral. This topic focuses on class and package diagrams, the two most important structural diagrams. Class diagrams help us model the low-level components of a system, while package diagrams help us model the high-level components.
Basic elements of a UML class diagram
Classes are modeled as rectangular boxes with one to four compartments, with the class name always in the first compartment. The class name distinguishes one class from another. If only the class name is needed, the class box will have only one compartment. A class name should start with a capital letter and be centered. If the class is abstract, its name should be written in italics. An abstract class is a class that cannot be instantiated directly.
Attributes are properties of a class that describe the possible values that instances of the class can have. A class can have zero or more attributes. They are listed in the second compartment. The attributes of a class can have an optional default value. For example, the default address for a student can be the school's address. The attributes of a class can be represented as: visibility name: type = default. Derived attributes, which are calculated from the values of other attributes, do not have default values.
Operations are the actions that a class can perform. They are analogous to the methods in a class. In other words, an operation is a general description of something that can be done to any object of a class. Operations are listed in the third compartment. A class can have zero or more operations. The enrol, drop, generateTranscript, and calculateGPA are the operations listed in the Student class.
The visibility defines how classes interact and are connected within a system. It specifies the accessibility of an element from other parts of the system. The four visibility options in UML are:
+(Public): The element is accessible from anywhere-(Private): The element is accessible only within the owning or parent class.#(Protected): The element is accessible within the owning (or parent) class and its subclasses (or children).~(Package): The element is accessible within the same package
In the Student class, the studentID attribute is private, the name attribute is public, the department attribute is protected, and the generateTranscript method is only visible to elements within the same package.
Multiplicity is another important concept in class diagrams. It is defined as how many instances of one class are related to a single instance of another class. It is shown using a range or specific numbers within brackets. In our example, the * on the Student side tells that a course can be taken by zero or more students, while the 1..* on the Course side tells that a student can register one or more courses. Class attributes can also have multiplicity. In our example, a student can only have one department, but they can have one or two addresses.
Modeling dependency relationships
Object-oriented modeling is based on three key relationships: dependencies, generalizations, and associations. Dependencies show how classes use each other. A dependency is represented as a dash line directed to the class being depended upon. The UML diagram below shows that the enrol and drop methods of the Student class are dependent on the Course class. When changes are made to the Course class, they may affect the operations of the Student class because of this dependency relationship.
Modeling generalization relationships
Generalizations link general classes to their more specific subclasses. In other words, generalization describes inheritance relationships between a parent (or super) class and its subclasses (or children). A child inherits the attributes and operations of its parents. The child can also have attributes and operations of its own.
A class can have zero or more parents. A class without any parent is described as a base class. A class with just one parent describes the single inheritance relationship where a derived class (child) inherits from a base class (parent). A generalization relationship is represented by an unfilled arrowhead directed at the parent class.
In the above example, the abstract Person class is the base class. The Student class inherits the attributes and operations of the Person class. As a result of the inheritance, the Student class can effectively substitute the Person class because it is a subtype of the Person class. The Student class also inherits the abstract getName() operation from the Person class. An abstract operation is a method declared in an abstract class but lacks a full implementation. It serves as a contract or a promise that subclasses or implementing classes must fulfill by providing their own implementations.
The concept of polymorphism comes into play when a child class provides its own implementation for an operation, effectively overriding the implementation inherited from its parent class. In our example, the Student::getName() operation overrides the Person::getName() operation.
A class with more than one parent describes the multiple inheritance relationship where one child inherits from more than one parent. In the example below, the Child class has two parents, and it inherits their attributes and operations.
Modeling association relationships
Associations show structural relationships between classes. They model their relationships and interactions. Binary associations are the most common type of association, but n-ary associations are also possible. N-ary associations connect more than two classes. Associations can be named to clarify the relationship between the classes they connect. A direction triangle can be used to indicate the direction in which the name should be interpreted. An association is shown as a solid line connecting two classes. The following named association can be interpreted as Student takes Course:
Multiplicity is an important concept in association relationships because it defines how many instances of one class are related to a single instance of another class. Association and attribute multiplicity have been discussed in the Basic elements of a UML diagram section.
The UML package diagram
Managing large-scale systems entails dealing with numerous classes, interfaces, components, and many other UML elements. As complexity grows, the imperative arises to group these entities into larger, more manageable units.
Packages are a way to organize UML elements into groups. Packages allow you to operate on the elements as a unit and control their visibility. Well-designed packages group related elements that tend to change together. A package is represented as a tabbed folder. The package name is displayed inside the folder unless the package contents are shown, in which case the package name is displayed in the tab. This is shown with the Product package in the following diagram:
Packages can be nested within other packages, creating a hierarchy. Top-level packages can be divided into subpackages, which can be further subdivided into sub-subpackages, and so on, until the hierarchy reaches classes and other UML elements. In the following example, the Product package is nested within the Sales package:
You use the fully qualified name to identify a class or other element in the package. The fully qualified name lists the package names according to the hierarchical structure until we arrive at the element. Double colons separate the listed names (::). Some examples of fully qualified names in our nested package include: Sales::Product::Order and Sales:Product::Discount.
Package diagram visibility
Package imports can be public or private. Public imports make imported elements visible outside of the importing namespace, while private imports do not. Public imports are represented with the <<import>> stereotype, while private imports are represented with the <<access>> stereotype. Public imports are the default type of import relationship.
When a package imports another package, its elements can use the elements of the imported package without using their fully qualified names. The imported package is called the target package. In the following example, the target package is Product:
In the above example, the Inventory package imports the Product package. The Product::Order class is made visible to the Inventory package since it is added to its namespace. The other classes are not made visible to the Inventory package because Product::Payment is private, Product::Shipping is protected, and Product::Discount is only accessible by elements in the Product package. In other words, only elements with public visibility in the target package are visible to the importing package's namespace.
Similarly, the Marketing package privately imports the Product package. It also only has access to the Product::Order. Two major differences arise when using <<import>> and <<access>>. First, with <<access>>, you have to specify the fully qualified name of the element privately imported. Second, the element privately imported has private visibility. Therefore, it cannot be visible to a package that imports from the Marketing package.
In the above example, the Warehouse package transitively imports the Order class from the Product package; however, the Promotion package does not import it because Product::Order class has private visibility.
In the following example, the Sales package contains the Amount and Order classes and the Product package nested inside it. Since the Amount class has public visibility, it is also visible to the Product package. The Sales::Order class also has public visibility. However, due to the naming conflict with Product::Order, you can only reference it with its fully qualified name.
Conclusion
In summary:
- Class and package diagrams are very important tools for modeling structural relationships.
- The basic elements of the class diagram, such as classes, attributes, operations, visibility, and multiplicity, provide the foundation for modeling low-level structural aspects of a system.
- Dependency, generalization, and association relationships are essential for capturing how classes interact and inherit attributes and behaviors.
- The UML package diagram is important for the organization and visibility control of related UML elements to effectively manage the large-scale system.
Structural diagrams are a key aspect of UML that focuses on representing a system's static structure or architecture. In this topic, we have discussed the two most commonly used structural diagrams. You have seen how class and package diagrams can help us model complex and sophisticated systems.