Composition
Composition vs aggregation vs association summary
Property | Composition | Aggregation | Association |
---|---|---|---|
Relationship type | Whole/part | Whole/part | Otherwise unrelated |
Members can belong to multiple classes | No | Yes | Yes |
Members existence managed by class | Yes | No | No |
Directionality | Unidirectional | Unidirectional | Unidirectional or bidirectional |
Relationship verb | Part-of | Has-a | Uses-a |
Composition in brief
- Types of Composition:
- Composition
- Aggregation
- To qualify as a composition, an object and a part must have the following relationship:
- The part (member) is part of the object (class)
- The part (member) can only belong to one object (class) at a time
- The part (member) has its existence managed by the object (class) (Death Relationship)
- The part (member) does not know about the existence of the object (class) (Unidirectional Relationship)
-
Composition models part-of relationships.
-
Composition has nothing to say about the transferability of parts.
- The parts of a composition can be Singular or Multiplicative.
- for example, a heart is a singular part of the body, but a body contains 10 fingers (which could be modeled as an array).
- If you can design a class using Composition, you should design a class using Composition.
-
Classes designed using Composition are straightforward, flexible, and robust (in that they clean up after themselves nicely).
- Variants on the Composition theme:
- A Composition may defer creation of some parts until they are needed.
- For example, a string class may not create a dynamic array of characters until the user assigns the string some data.
- A Composition may defer creation of some parts until they are needed.
- A Composition may opt to use a part that has been given to it as INPUT rather than create the part itself.
-
A Composition may DELEGATE Destruction of its parts to some other object (e.g. to a garbage collection routine).
- The key point here is that the Composition should manage it's parts without the user object needing to manage anything.
Sample Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <string>
#include <iostream>
class Point2D
{
private:
// these integers are a "part-of" Point2D class/object.
// there existance depends on Point2D object.
int m_x;
int m_y;
public:
// A default constructor
Point2D()
: m_x(0), m_y(0)
{
}
// A specific constructor
Point2D(int x, int y)
: m_x(x), m_y(y)
{
}
// An overloaded output operator
friend std::ostream& operator<<(std::ostream& out, const Point2D &point)
{
std::cout << "operator << in Point2D class" << std::endl;
out << "(" << point.m_x << ", " << point.m_y << ")";
return out;
}
// Access functions
void setPoint(int x, int y)
{
m_x = x;
m_y = y;
}
};
class Creature
{
private:
std::string m_name;
Point2D m_location;
public:
/*
A composition may opt to use a part that has been given to it as input rather than create the part itself.
- Here, "Point2D" is an input to "Creature" class.
- Life Cycle, memory management of this incoming "Point2D/location" object is responsibility of "main()" function, where it is actually created.
- Creature class will manage life span of "m_location" object.
*/
Creature(const std::string &name, const Point2D &location)
: m_name(name), m_location(location)
{
}
friend std::ostream& operator<<(std::ostream& out, const Creature &creature)
{
std::cout << "operator << in Creature class" << std::endl;
out << creature.m_name << " is at " << creature.m_location;
return out;
}
void moveTo(int x, int y)
{
m_location.setPoint(x, y);
}
};
int main()
{
std::cout << "Enter a name for your creature: ";
std::string name;
std::cin >> name;
Creature creature(name, Point2D(4, 7));
while (1)
{
// print the creature's name and location
std::cout << creature << '\n';
std::cout << "Enter new X location for creature (-1 to quit): ";
int x=0;
std::cin >> x;
if (x == -1)
break;
std::cout << "Enter new Y location for creature (-1 to quit): ";
int y=0;
std::cin >> y;
if (y == -1)
break;
creature.moveTo(x, y);
}
return 0;
}