Association (UML)
An Association between entities can be illustrated in UML with a solid line connecting the entities that are related.
Associations in UML are used to depict relationships and their multiplicity. Additional information, such as the Navigability or a Qualifier or the role, can be used to provide further details about the relationship.
Navigability
Associations illustrated with a solid line are by default bidirectional: Both entities are aware of each other and can query each entity at the opposite end the line. This inherent Bidirectionality can be made obvious by using open arrow-heads at each end of the line:
If an Association should not by bidirectional, only one arrow-head should be used at the end of the entity that is queryable, i.e. which is navigable:
Multiplicity
A Multiplicity at each end of the solid line indicates the number of associated entities of the given type. The default Multiplicity is 1 in the UML metamodel, so it is good practice to specify the multiplicity, with lower and upper bounds if required. Common forms of multiplicities are
Multiplicity | Description | Example |
---|---|---|
1 | Exactly one. | A has exactly one B |
* (or n ) | Any number, including zero. | A has none or more B |
0..1 | Zero or one. | A has none or one B |
1..* | One or more. | A has one or more B |
Qualified Association
Qualified Associations are used to provide information about an additional Qualifier that is used for querying an association. It is "the UML equivalent of a programming concept variously known as associative array, [...]" [📖UML, p. 74]
Example
The following Relationships between Address
, Company
, Employee
, Account
should be modelled with UML:
A
Company
has at least oneEmployee
A
Company
has at least oneAddress
, qualified through some kind of Address-type (e.g. invoicing, headquarter and so on)An
Employee
has exactly oneCompany
An
Employee
has exactly oneAddress
An
Employee
has exactly oneAccount
An
Account
has exactly oneEmployee
UML Diagram
The diagram uses the notation introduced above. The role owner
was added to the association Account -> Employee
to provide a deeper semantic context of the relationship.
Implementation
The implementation is rather naive as it forgoes possible infrastructure requirements (as in Company
's in-memory
storage of the Employee
s). Of interest is the implementation of the Qualified Association of the Company
's multiple
Address
es: Here, a Company
has a Usage Dependency with AddressType
to
realize the qualifier.
enum AddressType: string
{
case SHIPPING = "shipping";
case INVOICE = "invoice";
case MARKETING = "marketing";
case HEADQUARTER = "hq";
}
class Address
{
private string $street;
private string $city;
private string $country;
}
class Account
{
private Employee $owner;
private Money $balance;
}
class Employee
{
private Account $account;
private Company $company;
private Address $address;
}
class Employee
{
private array $addresses;
private array $employees;
public function getAddress(AddressType $addressType): ?Address
{
return $this->addresses[$addressType->value] ?? null:
}
}
Can Address
-instances be shared?
Company
and Employee
have an association with Address
, and therefor both of them are associated with "a same type", but do
they share the same object instances of Address
? The diagram is representing a class diagram, and the association does not provide further details about the nature of their relationship. Thus, they do not necessarily reference the same instance of Address
at first, just that they are associated with instances of this type. It makes no prediction if those instances are the same.
This would rather be an implementation detail of the target system. Here's how implementing it differently can have a severe impact to the system's data consistency, and where further notation with the help of UML can clarify its intent.
Referencing the same Address
as an Entity
If Company
and Employee
reference the same Entity, both point to one and the same uniquely identifiable instance of Address
. If Company
changes its address, e.g. the street
-name changes, those changes would be affecting the Employee
referencing the same Address
-(id)entity: The name of the street
where the Employee
lives changes, too. This is often referred to as Aliasing Bug.
Referencing the same Address
as a Value Object
If Company
and Employee
reference the same Value Object, both reference an immutable object: If Company
changes its address, e.g. the street
-name changes, a new Value Object is created and replaces the previous Address
-Object of Company
. The Employee
's place of residence is not affected by this change, since it still holds the reference to the original Value Object.
class Address
{
public function setStreet(string $street): Address
{
return new Address(
$street,
$this->city,
$this->country
);
}
}
class Employee
{
public function setAddress(Address $adress)
{
$this->address = $address;
}
}
class Company
{
public function setAddress(AddressType $addressType, Address $adress)
{
$this->addresses[$addressType->value] = $address;
}
}
class AddressAsValueObjectTest extends TestCase
{
public function testForReference()
{
$type = AddressType::SHIPPING;
$address = new Address("street", "city", "country");
$company->setAddress($address, $type);
$employee->setAddress($address);
$this->assertSame($company->getAddress($type), $employee->getAddress());
$company->setAddress($address->setStreet("moved to 23"), $type);
$this->assertNotSame($company->getAddress($type), $employee->getAddress());
}
}
see also