2. Związki pomiędzy obiektami

2.4. Kompozycja

W rzeczywistości, złożone obiekty często są budowane z mniejszych, prostszych obiektów. Na przykład samochód jest budowany z karoserii, silnika, czterech kół, skrzyni biegów, kierownicy, i jeszcze długo można by było wymieniać. Tak samo można powiedzieć o wielu wytworach techniki (toster zbudowany jest z obudowy, grzałek i mechanizmu wyrzutowego, smartfon ma CPU, pamięć, ekra, akumulator, itd...) czy biologii - człowiek ma  głowę, tułów, serce, wątrobę, itd.... Taki typ relacji nazywa się kompozycją. 

Mówiąc ogólnie, kompozycja obiektów modeluje relację "ma" pomiędzy dwoma obiektami. Samochód "ma" skrzynię biegów. Twój smartfon"ma" CPU, a człowiek "ma" serce (przynajmniej w sensie biologicznym). Złożony obiekt czasami jest nazywany całością. Prostszy obiekt jest często nazywany częścią lub składnikiem.

W C++ już widzieliście, że struktury i klasy mogą mieć składowe danych różnych typów (takich jak typy podstawowe lub inne klasy). Kiedy budujemy klasy które mają pola - to de facto konstruujemy złożony obiekt z prostszych części, co jest kompozycją. Z tego powodu, struktury i klasy są czasami nazywane typami złożonymi.

Kompozycja obiektów jest użyteczna w kontekście C++, ponieważ pozwala nam tworzyć złożone klasy, łącząc prostsze, łatwiej zarządzalne części. To redukuje złożoność i pozwala nam pisać kod szybciej i z mniejszą liczbą błędów, ponieważ możemy ponownie użyć kodu, który został już napisany, przetestowany i zweryfikowany jako działający.

Czasem występuje problem  z rozróżnieniem agregacji i kompozycji. W obu przypadkach prawidłowe określenia to "należy do" czy też "ma" - więc na analizie leksykalnej nie możemy się w pełni oprzeć. Raczej powinniście sprawdzić dwa założenia: 

  • Składnik może należeć tylko do jednego obiektu jednocześnie.
  • Czasem życia składnika zarządza obiekt nadrzędny. 

Dobrym przykładem kompozycji w życiu codziennym jest relacja między człowiekiem a wątrobą. Dana wątroba może być tylko częścią jednego człowieka - nie można jej współdzielić. Nie powstała także sama z siebie - tylko wraz z człowiekiem, i z nim zginie.

Zauważcie, że kompozycja nie ma nic do powiedzenia na temat przenośności części. Wątroba może być przeszczepiane z jednego ciała do drugiego - jednak nawet po przeszczepieniu, nadal spełnia wymagania kompozycji (jest teraz własnością innego człowieka - ale ciągle funkcjonuje tylko jako jego część - a nie samodzielny byt). 

Kompozycje w C++ implementuje się prosto i naturalnie - dając zwykłe pole w klasie. Kompozycje, które z wymagają dynamicznej alokacji / dealokacji pamięci (np składowa jest zbyt duża by mieścić się na stosie), mogą być implementowane przy użyciu wskaźników. W tym przypadku obiekt nadrzędny powinien być odpowiedzialny za wszystkie niezbędne wywołania new i delete (najlepiej zgodnie z RAII).

Kompozycja to częsty i preferowany związek. W ogólności -  jeśli można  zaprojektować klasę przy użyciu kompozycji, to powinna być tak zaprojektowana - pozwala to nie zaprzątać sobie głowy zarządzaniem czasem życia części składowych.