Historia pytania:
Oba terminy — kompozycja i agregacja — pochodzą z klasycznego projektowania obiektowego. Opisują relacje między obiektami: „całość–część”, ale różnią się stopniem powiązania obiektów.
Problem:
W dużych systemach ważna jest czytelność i rozszerzalność kodu. Kompozycja i agregacja pomagają budować poprawne zależności między klasami. Ważne jest zrozumienie różnicy — w przeciwnym razie można stworzyć zbyt sztywną lub, przeciwnie, nieczytelną architekturę.
Rozwiązanie:
Kompozycja — to silne powiązanie „część-całość”. Kiedy obiekt znajduje się tylko wewnątrz innego obiektu i nie może istnieć niezależnie. W Pythonie taki obiekt zazwyczaj tworzy się i zarządza nim wewnątrz klasy „kontenera”.
Agregacja — to słabsze powiązanie. Obiekt „część” może istnieć oddzielnie od obiektu „całości” i być do niego przekazywany z zewnątrz.
Przykład kodu:
class Silnik: def uruchom(self): print("Silnik uruchomiony") class Samochod: # Kompozycja: Silnik tworzymy wewnątrz Samochodu def __init__(self): self.silnik = Silnik() def jedź(self): self.silnik.uruchom() moj_samochod = Samochod() moj_samochod.jedź() class Kolo: pass class Rower: # Agregacja: koła są przekazywane z zewnątrz def __init__(self, kola): self.kola = kola k1, k2 = Kolo(), Kolo() rower = Rower([k1, k2])
Kluczowe cechy:
Czy usunięcie obiektu „kontenera” (np. Samochodu) usunie również obiekt Silnika?
W przypadku kompozycji, jeśli obiekt „część” nie jest już referencjonowany przez nikogo, zostanie usunięty przez zbieracz śmieci. A w przypadku agregacji obiekt „część” może nadal istnieć (może być referencjonowany przez inne obiekty).
Czy w Pythonie istnieje specjalna składnia dla agregacji i kompozycji, jak w innych językach?
W Pythonie nie ma słów kluczowych wskazujących na wyraźną kompozycję/agregację. Wszystko zależy od sposobu tworzenia i cyklu życia obiektów.
Czy można zamienić kompozycję na agregację i odwrotnie bez przepisywania logiki?
Nie zawsze. Jeśli logika wymaga pełnej kontroli i unikalności obiektu „części”, prosta zamiana kompozycji na agregację może prowadzić do błędów (np. jeśli koła różnych rowerów staną się wspólnymi instancjami jednej klasy).
Plusy:
Negatywny przypadek: W projekcie dla każdego samochodu tworzono zewnętrzny obiekt silnika i przekazywano go do samochodu przez agregację. Jednak później pomylono referencje do obiektów i losowo zamieniono silniki między samochodami — co doprowadziło do zamieszania i błędów.
Plusy: architektura wydawała się elastyczna. Minusy: trudno było zrozumieć, który silnik należy do którego samochodu.
Pozytywny przypadek: W innym projekcie silniki tworzone były wewnątrz klasy Samochód (kompozycja); samochody bezpośrednio zarządzały swoimi silnikami, co zapewniło niezawodność.
Plusy: brak wycieków i zamieszania logicznego. Minusy: trzeba było napisać nieco więcej kodu do zarządzania cyklem życia każdego samochodu i jego części.