In computer science, object composition and object aggregation are closely related ways to combine objects or data types into more complex ones. In conversation, the distinction between composition and aggregation is often ignored. Common kinds of compositions are objects used in object-oriented programming, tagged unions, sets, sequences, and various graph structures. Object compositions relate to, but are not the same as, data structures.

Object composition refers to the logical or conceptual structure of the information, not the implementation or physical data structure used to represent it. For example, a sequence differs from a set because (among other things) the order of the composed items matters for the former but not the latter. Data structures such as arrays, linked lists, hash tables, and many others can be used to implement either of them. Perhaps confusingly, some of the same terms are used for both data structures and composites. For example, "binary tree" can refer to either: as a data structure it is a means of accessing a linear sequence of items, and the actual positions of items in the tree are irrelevant (the tree can be internally rearranged however one likes, without changing its meaning). However, as an object composition, the positions are relevant, and changing them would change the meaning (as for example in cladograms).

Programming technique

Object-oriented programming is based on using objects to encapsulate data and behavior. It uses two main techniques for assembling and composing functionality into more complex ones, sub-typing and object composition. Object composition is about combining objects within compound objects, and at the same time, ensuring the encapsulation of each object by using their well-defined interface without visibility of their internals. In this regard, object composition differs from data structures, which do not enforce encapsulation.

Object composition may also be about a group of multiple related objects, such as a set or a sequence of objects. Delegation may enrich composition by forwarding requests or calls made to the enclosing composite object to one of its internal components.

In class-based and typed programming languages, types can be divided into composite and non-composite types, and composition can be regarded as a relationship between types: an object of a composite type (e.g. car) "has" objects of other types (e.g. wheel). When a composite object contains several sub-objects of the same type, they may be assigned to particular roles, often distinguished by names or numbers. For example, a Point object might contain 3 numbers, each representing distance along a different axis, such as 'x', 'y', and 'z'. The study of part-whole relationships in general, is mereology.

Composition must be distinguished from subtyping, which is the process of adding detail to a general data type to create a more specific data type. For instance, cars may be a specific type of vehicle: car is a vehicle. Subtyping doesn't describe a relationship between different objects, but instead, says that objects of a type are simultaneously objects of another type. The study of such relationships is ontology.

In prototype-based programming languages such as JavaScript, objects can dynamically inherit the behaviors from a prototype object at the moment of their instantiation. Composition must be distinguished from prototyping: the newly instantiated object inherits the composition of its prototype, but it may itself be composed on its own.

Composite objects may be represented in storage by co-locating the composed objects, by co-locating references, or in many other ways. The items within a composite object may be referred to as attributes, fields, members, properties, or other names, and the resulting composition as composite type, storage record, structure, tuple, or a user-defined type (UDT). For details, see the aggregation section below.

UML modeling technique

alt=A bycicle class represented in UML, with three properties: saddle, wheels and parts, the two last having a multiplicity indicating several objects|thumb|Object composition using UML properties to compose objects

In UML modeling, objects can be conceptually composed, independently of the implementation with a programming language. There are four ways of composing objects in UML: property, association, aggregation and composition:

  • A property represents an attribute of the class.
  • An association represents a semantic relationship between instances of the associated classes. The member-end of an association corresponds to a property of the associated class.
  • An aggregation is a kind of association that models a part/whole relationship between an aggregate (whole) and a group of related components (parts).
  • A composition, also called a composite aggregation, is a kind of aggregation that models a part/whole relationship between a composite (whole) and a group of exclusively owned parts.

The relationship between the aggregate and its components is a weak "has-a" relationship: The components may be part of several aggregates, may be accessed through other objects without going through the aggregate, and may outlive the aggregate object.

Special forms

Containment

<!-- Courtesy note per WP:RSECT: Containment (computer Programming) and others link here]] -->

Composition that is used to store several instances of the composited data type is referred to as containment. Examples of such containers are arrays, associative arrays, binary trees, and linked lists.

In UML, containment is depicted with a multiplicity of 0..* or 1..*, indicating that the composite object is composed of an unknown number of instances of the composed class.

Recursive composition

Objects can be composed recursively, and their type is then called recursive type. Examples includes various kinds of trees, DAGs, and graphs. Each node in a tree may be a branch or leaf; in other words, each node is a tree at the same time when it belongs to another tree.

In UML, recursive composition is depicted with an association, aggregation or composition of a class with itself.

Composite pattern

The composite design pattern is an object-oriented design based on composite types, that combines recursive composition and containment to implement complex part-whole hierarchies.

Composite types in C

This is an example of composition in C.

<syntaxhighlight lang="C">

struct Person

{

int age;

char name[20];

enum {job_seeking, professional, non_professional, retired, student} employment;

};

</syntaxhighlight>

In this example, the primitive (noncomposite) types , } and the composite array type are combined to form the composite structure . Each structure then "has an" age, name, and an employment type.

Timeline of composition in various languages

C calls a record a struct or structure; object-oriented languages such as Java, Smalltalk, and C++ often keep their records hidden inside objects (class instances); languages in the ML family simply call them records. COBOL was the first widespread programming language to support records directly; ALGOL 68 got it from COBOL and Pascal got it, more or less indirectly, from ALGOL 68. Common Lisp provides structures and classes (the latter via the Common Lisp Object System).

;1959 – COBOL

<syntaxhighlight lang="COBOL">

01 customer-record.

03 customer-number pic 9(8) comp.

03 customer-name.

05 given-names pic x(15).

05 initial-2 pic x.

05 surname pic x(15).

03 customer-address.

05 street.

07 street-name pic x(15).

09 house-number pic 999 comp.

05 city pic x(10).

05 country-code pic x(3).

05 postcode pic x(8).

03 amount-owing pic 9(8) comp.

</syntaxhighlight>

;1960 – ALGOL 60

Arrays were the only composite data type in Algol 60.

;1964 – PL/I

<pre>

dcl 1 newtypet based (P);

2 (a, b, c) fixed bin(31),

2 (i, j, k) float,

2 r ptr;

allocate newtypet;

</pre>

;1968 – ALGOL 68

<pre>

int max = 99;

mode newtypet = [0..9] [0..max]struct (

long real a, b, c, short int i, j, k, ref real r

);

newtypet newarrayt = (1, 2, 3, 4, 5, 6, heap real := 7)

</pre>

For example, a linked list might be declared as:

<pre>

mode node = union (real, int, compl, string),

list = struct (node val, ref list next);

</pre>

For ALGOL 68 only the type name appears to the left of the equality, and most notably the construction is made – and can be read – from left to right without regard to priorities.

;1970 – Pascal

<syntaxhighlight lang="Pascal">

type

a = array [1..10] of integer;

b = record

a, b, c: real;

i, j, k: integer;

end;

</syntaxhighlight>

;1972 – K&R C

<syntaxhighlight lang="C">

  1. define max 99

struct newtypet {

double a, b, c;

float r;

short i, j, k;

} newarrayt[10] [max + 1];

</syntaxhighlight>

;1977 – FORTRAN 77

Fortran 77 has arrays, but lacked any formal record/structure definitions. Typically compound structures were built up using or statements:

<syntaxhighlight lang="FORTRAN">

CHARACTER NAME*32, ADDR*32, PHONE*16

REAL OWING

COMMON /CUST/NAME, ADDR, PHONE, OWING

</syntaxhighlight>

;1983 – Ada

<syntaxhighlight lang="ADA">

type Cust is

record

Name : Name_Type;

Addr : Addr_Type;

Phone : Phone_Type;

Owing : Integer range 1..999999;

end record;

</syntaxhighlight>Ada 95 brought OOP concepts through tagged types (the equivalent of a C++ class), Ada 2012 added support for substitution verification through class-wide contracts.

;1983 – C++

<syntaxhighlight lang="cpp">

const int max = 99;

class {

public:

double a, b, c;

float &r;

short i, j, k;

}newtypet[10] [max + 1];

</syntaxhighlight>

;1991 – Python

<syntaxhighlight lang="python">

max = 99

class NewTypeT:

def __init__(self):

self.a = self.b = self.c = 0

self.i = self.j = self.k = 0.0

  1. Initialise an example array of this class.

newarrayt = [[NewTypeT() for i in range(max + 1)] for j in range(10)]

</syntaxhighlight>

;1992 – FORTRAN 90

Arrays and strings were inherited from FORTRAN 77, and a new reserved word was introduced: type

<syntaxhighlight lang="fortran">

type newtypet

double precision a, b, c

integer*2 i, j, k

  • No pointer type REF REAL R

end type

type (newtypet) t(10, 100)

</syntaxhighlight>

FORTRAN 90 updated and included FORTRAN IV's concept called NAMELIST.

<syntaxhighlight lang="fortran">

INTEGER :: jan = 1, feb = 2, mar = 3, apr = 4

NAMELIST / week / jan, feb, mar, apr

</syntaxhighlight>

;1994 – ANSI Common Lisp

Common Lisp provides structures and the ANSI Common Lisp standard added CLOS classes.

<syntaxhighlight lang="lisp">

(defclass some-class ()

((f :type float)

(i :type integer)

(a :type (array integer (10)))))

</syntaxhighlight>

For more details about composition in C/C++, see Composite type.

See also

  • Composite type
  • Composition over inheritance
  • Decomposition (computer science)
  • Delegation (programming)
  • Function composition (computer science)
  • Implementation inheritance
  • Inheritance semantics
  • Has-a
  • Law of Demeter
  • Object-oriented analysis and design
  • Virtual inheritance

References

  • Association, Aggregation and Composition, accessed in February 2009
  • Harald Störrle, UML2, Addison-Wesley, 2005