A composition is an association that represents a strong whole-part relationship. When the whole is destroyed, parts are destroyed too i.e., the part should not exist without being attached to a whole.
A Board
(used for playing board games) consists of Square
objects.
Composition also implies that there cannot be cyclical links.
The ‘sub-folder’ association between Folder
objects is a composition type association. That means if the Folder
object foo
is a sub-folder of Folder
object bar
, bar
cannot be a sub-folder of foo
.
Whether a relationship is a composition can depend on the context.
Is the relationship between Email
and EmailSubject
composition? That is, is the email subject part of an email to the extent that an email subject cannot exist without an email?
- When modeling an application that sends emails, the answer is 'yes'.
- When modeling an application that gather analytics about email traffic, the answer may be 'no' (e.g., the application might collect just the email subjects for text analysis).
A common use of composition is when parts of a big class are carved out as smaller classes for the ease of managing the internal design. In such cases, the classes extracted out still act as parts of the bigger class and the outside world has no business knowing about them.
Cascading deletion alone is not sufficient for composition. Suppose there is a design in which Person
objects are attached to Task
objects and the former get deleted whenever the latter is deleted. This fact alone does not mean there is a composition relationship between the two classes. For it to be composition, a Person
must be an integral part of a Task
in the context of that association, at the concept level (not simply at implementation level).
Identifying and keeping track of composition relationships in the design has benefits such as helping to maintain the data integrity of the system. For example, when you know that a certain relationship is a composition, you can take extra care in your implementation to ensure that when the whole object is deleted, all its parts are deleted too.
Implementing composition
Composition is implemented using a normal variable. If correctly implemented, the ‘part’ object will be deleted when the ‘whole’ object is deleted. Ideally, the ‘part’ object may not even be visible to clients of the ‘whole’ object.
class Email {
private Subject subject;
...
}
class Email:
def __init__(self):
self.__subject = Subject()
In this code, the Email
has a composition type relationship with the Subject
class, in the sense that the subject is part of the email.