Tutorials
OO PHP Part 3: UML, Classes and Relations
Preface
Welcome to part 3 of this series.
There are many design theories, documented practices and paradigms. Before attempting to dive into any of those, it’s wise to have knowledge of a common way to communicate programming structures. UML provides an industry standard to do so.
There are many types of structures than can be communicated, and UML provides a standard for many of them. However in this article I will focus on one particular type of diagrams, class diagrams.
For this tutorial I recommend having a UML editor to experiment with recreating the given examples. The goal of this tutorial is to try to provide you the most commonly used toolset to start creating your own Class Diagrams. A list of editors is included in chapter 5.
This tutorial will allow you to better understand class relations, and the diagrams I will be using in the future tutorials, starting with some common Design Patterns in the next article.
Index
1. Introduction
1.1 What are class diagrams?
1.2 Class Diagrams: OOA versus OOD
2. Class Diagram Entities
2.1 Classes
2.2 Interfaces
2.3 Packages
2.4 Data types
2.5 Entity visibility
3. Relations
3.1 Associations
3.1.1 Bi-directional navigable association
3.1.1a Bi-directional association: code example
3.1.2 Uni-directional navigable association
3.1.2a Uni-directional association: code example
3.1.3 Association class
3.1.3a Association class: code example
3.2 Aggregation (aggregate association)
3.2.1 Regular aggregation
3.2.1a Aggregation: code example
3.2.2 Composite Aggregation (composition)
3.2.2a Composite Aggregation: code example
3.3 Generalizations
3.4 Dependencies
4. Stereotypes
5. In conclusion
1. Introduction
1.1 What are class diagrams?
Class diagrams are representations of entities and their relations. These entities most commonly represent a class or interface like they exist in your code, but can also refer to an entity in a more abstract sense, even a type of data.
These are all static entities. A class diagram doesn’t reflect the state an entity is in. A class diagram featuring object instances is commonly referred to as an Object Diagram.
1.2 Class Diagrams: OOA versus OOD
At this stage, I don’t want to get too much into the details of either Object Orientated Analysis or Object Orientated Design, but, I will give a brief insight into how these concepts relate to Class Diagrams.
Three perspectives have been defined to look at Class Diagrams:
- Conceptual Perspective
- Specification Perspective
- Implementation perspective
The conceptual perspective is used to gain insight into the needed functions and properties in a system. These are not necessarily the actual properties of the eventual system. The entities in the diagrams are conceptual; they may or may not represent the eventual entities in the system. This perspective is used for analysis (OOA), and is commonly the next step after a basic Requirement Analysis.
The (rarely used) specification perspective presents a solution, a design (OOD), but doesn’t yet commit to a specific language implementation.
The implementation perspective gets into the nitty gritty details of the design, accounting for language specifics. This design can eventually be translated into a code skeleton (many UML editors offer a code generation feature).
In practice you may find diagrams that have properties of more than one perspective.
2. Class Diagram Entities
Quick overview of how the entities in class diagrams are presented.
2.1 Classes
The visual representation of a class is simple: it’s a rectangle box with three possible compartments.
- The top for the name of the class and optionally its stereotype (see section stereotypes)
- The middle compartment is a list of the class properties, referred to as attributes
- The bottom compartment is for the operations, in most applications of UML equal to methods in actual code. If an operation is underlined, that means it is a static operation
Presentation of abstract classes is identical, with the exception of the name of the class, which should be displayed italic.
2.2 Interfaces
Interfaces are very similar in presentation to classes. In fact, in terms of UML, an interface is a class. Interfaces don’t have properties or attributes, so the entity has only two compartments. In addition the stereotype <<interface>> is added, so no confusion with a class without attributes is possible.
There is an alternate notation that uses circles (also referred to as ‘lollipop’ notation), but it is unsupported by many UML editors. The lollipop notation doesn’t specify operations, and is only used for Object Oriented Analysis.
2.3 Packages
A package is an entity encapsulating and grouping other entities like classes and interfaces. PHP has no package scope, yet classes are commonly organized in fashion that give indication of packages. In terms of UML, packages look like this:
2.4 Data types
Data types are entities too. The previous entitles can also be referred to as elements, data types can not: data types don’t have a single representation in a class diagram. Instead they are referenced with the specification of an attribute or in the calling routine (applying to arguments) or return value of an operation.
Above indicates attribute someAttribute to be of type string, as well as the argument someArgument in the calling routine of someOperation. Operation someOperation is specified to return a value of data type Boolean.
Data types represent entities native to a certain programming language, not all languages support the same set of data types, and when they do their implementations can vary. UML is meant to be extensible; you may define your own data types within UML.
2.5 Entity visibility
UML class diagrams allow you to specify the visibility of entities.
Mark | Visibility type |
---|---|
+ | Public |
# | Protected |
- | Private |
~ | Package |
This can be compared with visibility as definable in PHP 5, with the addition of the ‘package’ visibility type, which limits the visibility of a class member to the package it is part of. PHP knows no ‘package scope’.
Visibility is not limited to class members. Packages can have visibility too. At times, when you need to look at ‘the bigger picture’ you may want to create a package diagram. These won’t be covered in this article though.
3. Relations
Visualizing entities separately is pretty useless; the most important use of class diagrams is to visualize the relations between these entities.
Below is a list of the most common types of relations, which I will discuss in the following sections.
- Associations
- Bi-directional association (standard association)
- Uni-directional association
- Association class (drop class)
- Aggregation
- Regular aggregation
- Composition (composite aggregation)
- Generalizations
- Inheritance
- Implementation (realization)
- Dependencies
3.1 Associations
An association is a linkage between two classes. An association has navigability , indicating ‘awareness’ of one class of the existence of another.
The parties involved in the association play a role in the association, these roles can be named.
For each association end you can also specify a multiplicity value, restricting the number of times the class at that end may exist, in that relation.
Multiplicity Indicator | Meaning |
---|---|
0..1 | Zero or one |
1 | One only |
0..* | Zero or more |
1..* | One or more |
n | Only n (where n > 1) |
0..n | Zero to n (where n > 1) |
1..n | One to n (where n > 1) |
Below is a figure showing the notation of these association properties:
All of these properties are optional, but specifying them can tell a lot about an association. If you don’t specify the multiplicity, it is assumed to be 1.
NOTE: This chapter will include some code examples. It is important to remember that when translating anything abstract to something concrete, interpretation is a factor. Note that the code examples are just that, examples, and not definitions of the concepts they interpret.
3.1.1 Bi-directional navigable association
Associations are assumed to be bi-directional unless indicated otherwise. Bi-directional implicates that both classes are aware of their relation to the other class. This is indicated by a plain solid line:
In this example, both ShoppingCart and Customer are aware of each other’s existence and may interact accordingly. Although ShoppingCart relates to Customer and vice versa, neither is part of the other, they are both allowed to be part of the bigger whole.
Note the multiplicity. It’s bit of an unconventional example: it allows more than one Customer per ShoppingCart.
The following diagram was made with Visual Paradigm for UML Community Edition, which has full UML 2 support.
The diagram shows a uni-directional association between Xml_Parser_Interface and all of the classes implementing it, and Xml_Implementation_Abstract and all of its children. Parties on both sides of the association are aware of the existence of the other side, and their relation.
One thing you may notice is the small closed arrowhead, next to the association label. This is a UML 2 feature, a label direction indicator. This doesn’t indicate navigability, only how the label should be read.
3.1.1a Bi-directional association: code example
class Customer { public function getCheckOutDetails(ShoppingCart $cart) { $this->checkCredit( $cart->getAmount() ); //. } } class ShoppingCart { public function checkOutUsingCustomer(Customer $customer) { $customer->getCheckOutDetails($this); //. } }
Note that both classes are aware of each other’s interface.
3.1.2 Uni-directional navigable association
In a uni-directional relation only one class is ‘aware’ that the relationship exists.
In this example, class ShoppingCart is not aware of its relation to Customer, yet Customer is aware of ShoppingCart and the fact that there is a relation. Note that we kept the multiplicity value, meaning a multitude of Costumer is aware of a single ShoppingCart. The single instance of ShoppingCart will not be aware of its relation to the Customers.
In terms of navigability, this relation can be referred to as ‘Customer to ShoppingCart’. When we reverse the navigability, the diagram tells a different story.
Now a single ShoppingCart relates to a multitude of Customers, without them knowing. You’ll find it is more common to allow multiplicity on the ‘unaware’ side of an Uni-directional association.
3.1.2a Uni-directional association: code example
class Customer { public function getCreditDetails($purchaseAmount) { $this->checkCredit($purchaseAmount); //. } } class ShoppingCart { public function checkOutUsingCustomer(Customer $customer) { $customer->getCheckOutDetails($this->getAmount()); //. } }
In contradiction to the previous example, Customer is unaware of ShoppingCart.
3.1.3 Association class
An association class (or 'drop class') is a class that is relevant to a specific association.
The dotted line in the above example indicates that whenever an instance of Customer is related to an instance of ShoppingCart, an instance of WishList will exist. The relation between Customer and ShoppingCart depends on the existence of WishList.
A drop class can apply to any relation, including aggregate associations (see the next section) with any navigability.
The circle at the intersection is optional.
3.1.3a Association class: code example
class Customer { public function checkoutCart(ShoppingCart $cart) { $cart->getItems(); //. return $items; } } class ShoppingCart { public function getItems() { //. } } class WhishList { public function makeCheckOutList(Customer $customer, ShoppingCart $cart) { $this->updateWhishList( $customer->checkoutCart($cart) ); //. } }
The relation between Customer and ShoppingCart is maintained by WhishList.
3.2 Aggregation (aggregate association)
Aggregation, as the term implies, indicates that one is part of another. These parties are referred to as the whole and the part, where the encapsulating class is the whole. In terms of actual code, this normally means an instance of one entity is encapsulated by another in an object property.
3.2.1 Regular aggregation
In regular aggregation, the lifecycle of the part is independent of the whole. This is also referred to as a ‘has a’ relationship. Notation: An open diamond shape on the side of the whole.
Below is a scenario that you can probably easily relate to.
A single instantiation of Customer encapsulates a single ShoppingCart, in the role of currentCart. However, Customer is passed an existing instantiation of ShoppingCart, making it independent from the Customer instantiation.
3.2.1a Aggregation: code example
class Customer { private $_cart; public function __construct(ShoppingCart $cart) { $this->_cart = $cart; } } class ShoppingCart { }
3.2.2 Composite Aggregation (composition)
In composite aggregation the whole is composed of parts which lifecycles are dependant on the whole.
There should be a strict relation in which the whole is composed of it’s parts.
- A car has wheels, but four wheels don’t make a car.
- A star cluster is composed of stars, and the collection of stars in it make the cluster.
Example 2 adheres to this strict rule. This rule is impossible to apply in it’s strictest sense, so consider the following example as a sensible compromise:
The USA is composed of states.
Arguably the collection of states doesn’t equal the United States, but it does have a stronger relation than regular aggregation: the whole (the USA) can not exist without it’s parts. A car can exist without wheels, a customer can exist without a shopping cart.
Think about the strength of the relation before you decide to apply lifecycle dependency. Not the other way around: the lifecycle dependency shouldn’t be adding to the strength of the relation when modelling the relation, it is merely a result of the strong relation.
If the relation is weaker, regular aggregation is the better choice: other components may want to use the part(s) when development progresses. Which may be impossible if you make the part’s lifecycle dependant on the whole.
To stick with our e-commerce example: in our system, we decided that a ShoppingCart cannot exist without a Customer (which makes sense). We also decided that the ShoppingCart is the centre of our Universe, and make it the whole in our relation.
The result of these decisions is that a Customer cannot exist without ShoppingCart either. This is an e-commerce system, if you’re not buying, get out. :P
Whether these are good choices, I’ll leave in the middle, this is just for demonstration purposes.
3.2.2a Composite Aggregation: code example
//Uni-directional class Customer { private $_cart; public function __construct() { $this->_cart = new ShoppingCart(); } } class ShoppingCart { private $_items = array(); public function getItems() { return $this->_items; } } //Bi-directional class Customer { private $_cart; public function __construct() { $this->_cart = new ShoppingCart(); } public function getCart() { return $this->_cart; } public function checkOutCart() { $this->getCart()->getCheckOutDetails($this); } public function getCustomerDetails() { //. } } class ShoppingCart { public function getCheckOutDetails(Customer $customer) { $customer->getCustomerDetails(); //. } }
3.3 Generalizations
Also referred to as ‘is a’ relationships, these are the relationships you should already be familiar with: inheritance and implementations. I’ll give you a quick example of the notation of both:
Abstract class ProductList implements interface ItemList, ShoppingCart extends ProductList.
3.4 Dependencies
Dependencies indicate that change in the structure of one may require a change in the other.
In many cases, other types of relations already imply some form of dependency, but if a more descriptive relation is not applicable, you can use dependency to establish relations between elements. This implies that dependency is a ‘weaker’ and less descriptive relation than associative relations.
Consider the following diagram:
Customer is dependant on WhishList because it fetches an array containing wish list items. The underlined operation indicates that getWishList is a static operation. Customer is dependant on the signature (calling routine and return value) of getWishList.
In contradiction to associations, dependencies can not be labelled, nor can the roles of the elements involved in the relation. Dependencies also do not necessarily relate to entities able to be instantiated (in which cases an association is more applicable), also to those that can not, like packages or static operations. Multiplicity is not applicable to dependencies.
To get a look at the dependencies in a larger scope, a package diagram is essential. A package diagram is essentially a class diagram only showing packages and their dependencies.
This package diagram show two packages: e-commerce and user_management. e-commerce is dependant on user_management, for things like authentication and the like. This particular dependency seems unavoidable, but one of your main goals when designing an application should be to reduce dependency between elements, and making them interchangeable. Bear in mind that a class diagram can not specify the level of dependency, something that can be greatly reduced but not eliminated, by enforcing an strict interface.
Package diagrams can also feature other class diagram relations and ‘subsystem’ package elements, but that is out of the scope of this article.
4. Stereotypes
The OMG UML specification states:
"A stereotype is, in effect, a new class of metamodel element that is introduced at modeling time. It represents a subclass of an existing metamodel element with the same form (attributes and relationships) but with a different intent. Generally a stereotype represents a usage distinction..."
Stereotypes introduce distinctions between (types of) model elements, by sub-classification of model elements. Like the bold text in the quote notes, this is usually a usage distinction. When talking about elements, I am referring to UML entities that have a single representation in the model, like classes and associations.
UML has predefined stereotypes, and it permits the use of custom (user defined) stereotypes that are not supported by the UML metamodel. Some build-in ones we already encountered: interface, implementation and realize.
Applying a stereotype can be compared to applying inheritance in OOP. When a stereotype is applied, a specialized model element is created: when I apply the stereotype <<interface>> to a class without attributes, it represents an interface. When I apply the stereotype <<framework>> to a package, it represents a framework. Pretty simple actually, right?
Ok, so I simplified it a whole bit. More information about the UML ‘four-layer metamodel architecture’ can be found here [http://www-inf.int-evry.fr/COURS/UML/semantics/semant2.html], I’m not covering it in this article. Most UML editors ignore the semantics described by the UML specification, more information in this [http://www.ii.uib.no/~rolfwr/thesisdoc/main1.html] excellent thesis.
5. In conclusion
As said in the preface, this was a basic introduction, to provide you with a toolset to start creating UML class diagrams. The basic knowledge gained in this tutorial will also allow you to understand the class diagrams you will find in the next tutorial.
I hope you found this one useful, and will come back for part four.