The composition establishes a special link between the two classes, in which the existence of the objects of the class part becomes dependent on that of the objects of the class whole. The use of compositions is therefore indicated in all those cases where the relationship between the two classes is not equal, and we want to express in a more stringent way the subalternity of one of the two classes with respect to the other.
The role pointing to the class whole is never navigable; in the Designer it is represented by a black rhombus, which graphically distinguishes a composition from an association. The only multiplicity allowed for this role is Exactly one (
1); this is because each object of class part can only be part of one object within the application.
The other end of the composition, the one of the role of the part class, on the other hand, is always navigable and can have any multiplicity. In other words, a part object can be part of a single integer (whole), while an arbitrary number of parts can be defined for each integer.
An example of composition is shown in the figure: the Project_assignment class has been modeled as a part of the whole Employee class; this is because the assignment relative to a project is not an independent concept, but is always subordinate to that of employee. In other words, assignments only make sense to exist as objects as parts of specific instances of the Employee class.
Create a composition #
Right-click on the whole class to open its
Class menu and select the
New composition option; a dotted line will appear from the whole class to your cursor position. Move to the part class and click on it.
Alternatively, click on the Palette
Create a new composition, then click first on the whole class and then on the part class.
The moment a composition is created, the class that becomes a part loses its class role, and with it the ability to be managed independently in the generated application:
- on this class it is no longer possible to create objects independently (directly from the class form), but only by defining them as parts of an object that is being created or modified within the whole class;
- it is not possible to associate the part objects already created, because only one whole object can correspond to each of them;
- when an object of whole class is deleted, the instances of part class associated with it are also deleted.
Reflexive Compositions #
You can also create reflexive compositions, where a class is both whole and part of itself. This can be useful if you want to model a taxonomy, or any case where a class represents a node in a hierarchy of objects. The procedure for creating a reflexive composition is analogous to that for reflexive association; however, it is necessary that the class on which the reflexive composition is declared has at least one relation to another class. If this condition is not met, the Designer signals us that each connected group of classes in the model must include at least one class that is not part of any other. In order for the reflexive composition to be reachable in the generated application, the relationship (of any type) that connects it to the other class must be navigable from the other class to the class with the reflexive composition. Taking the taxonomy example again, in other words, the root of the hierarchy must be modeled on another non-reflective class.
Convert an association into a composition #
A composition can always be converted to an association via the
Convert to association option of its
Composition menu. The role cardinality of what was previously the whole class is automatically set to Exactly one (
Right-click on the association you want to convert to open its
Association menu and select the
Convert to composition option. The role with multiplicity Exactly one (
1) will be that of class whole and will appear as a black rhombus. The direction of navigability will be automatically corrected so that it points to the class part.
Example: to-many composition #
In the model in figure we have represented the customers (Customer) of a digital store and the payment methods they use (Payment_method). We have modeled the relationship between the two classes through a composition: since the presence in the system of data related to credit cards is dependent on the single record of the customer, Customer is the whole class, while Payment_method is the part class; it would not make sense to manage the payment methods independently, keeping track of the cards that belong to customers who no longer use the application (and therefore have been deleted from the database).
Since each customer may have use multiple payment methods, we set Any (
*) as the multiplicity for the role of the part class.
Example: to-one composition #
Let’s now expand the previous example by modeling the customers’ home address, another concept that is heavily dependent on Customer. Unlike payment methods, which could be more than one for each customer, each object in Customer can only correspond to one address. For this reason, we have chosen as multiplicity Zero or one (
01); compared to a more stringent multiplicity like Exactly one (
1), Zero or one allows to add the address also later (but still from the form of Customer).
We then added the Employee class to the model, to create a registry of the employees in our application. Instead of modeling a separate class to store their addresses, we preferred to reuse the existing Address class. We thus created another composition with multiplicity Zero or one (
01), which connects Employee to Address.
Now the Address class is part of both Employee and Customer,but this of course does not mean that employee and customer must share the same address! On the contrary, given the nature of compositions, an object of Address can never be part of both an Employee and a Customer. When a part class is involved in multiple compositions, each of its objects can only be part of one whole object in the entire application.
In the case of multiple compositions like this, the Designer allows us to define a specific uniqueness constraint (Unique constraint options for parts), based on the origin of the part object. This way we can prevent the information of one class from conflicting with that of another.