Delegowanie odpowiedzialności przez kompozycję to praktyka programistyczna, w której obiekt klasy zawiera obiekt innej klasy (lub klas) i wykorzystuje je do realizacji części swojej logiki, zamiast dziedziczyć ich interfejs.
Wczesne wersje OOP kładły nacisk na dziedziczenie, jednak z biegiem czasu praktyka pokazała, że kompozycja często zapewnia większą elastyczność, rozszerzalność i zmniejszenie powiązań między komponentami.
Dziedziczenie sztywno łączy obiekty: zmiany w klasie bazowej wpływają na wszystkich potomków, hierarchie stają się skomplikowane, co prowadzi do kruchości architektury. Kompozycja eliminuje te problemy, pozwala budować bardziej niezawodne i łatwiejsze do utrzymania systemy.
W C++ delegowanie realizowane jest poprzez włączenie obiektu jednej klasy jako członka innej klasy. W klasie opakowującej wywoływane są metody obiektu wewnętrznego.
Przykład kodu:
class Logger { public: void log(const std::string& msg) { std::cout << msg << std::endl; } }; class FileProcessor { Logger logger; // Kompozycja public: void process(const std::string& filename) { logger.log("Przetwarzanie pliku: " + filename); // ... } };
Cechy kluczowe:
Czy kompozycja może całkowicie zastąpić dziedziczenie?
Nie, dziedziczenie jest potrzebne tam, gdzie wymagana jest relacja „jest” (is-a), kompozycja — tam gdzie „ma” (has-a). Na przykład, Button dziedziczy po Widget, ale Car „ma” Engine (kompozycja).
Czy w kompozycji można zmienić zachowanie delegowanej metody?
Tak, metody delegowania można dostosować, nie dotykając klasy źródłowej. Można też dynamicznie zmieniać delegowany obiekt (na przykład przez wskaźnik lub unikalny wskaźnik).
Czy kompozycja jest wolniejsza od dziedziczenia?
Nie, w większości przypadków nie ma różnicy w wydajności. Czasami dziedziczenie wiąże się z kosztami przez wywołania wirtualne (vtable), a kompozycja — tylko z rozmiarem obiektu.
W projekcie wszystkie okna dialogowe dziedziczyły z ogólnego DialogWindow. Dodanie nowej logiki biznesowej prowadziło do nie działającego kodu we wszystkich potomkach.
Zalety:
Wady:
Wspólne funkcje wyodrębnione do oddzielnych klas (logowanie, walidacja), które są wstrzykiwane do każdego dialogu przez kompozycję.
Zalety:
Wady: