Kompozycja i dziedziczenie to dwa różne podejścia do projektowania oprogramowania, a wybór między nimi zależy od specyfiki projektu. Oto przykład użycia kompozycji zamiast dziedziczenia:
Załóżmy, że projektujemy aplikację do obsługi sklepu internetowego, która ma obsługiwać różne rodzaje produktów. Każdy produkt ma wiele atrybutów, takich jak nazwa, opis, cena, kolor, waga, itp. Chcemy również umożliwić klientom dodawanie produktów do koszyka i dokonywanie zamówień.
W podejściu z dziedziczeniem, moglibyśmy stworzyć klasę bazową „Product” i klasy pochodne dla każdego rodzaju produktu, np. „Book”, „Electronics”, „Clothing”, itp. Każda z tych klas pochodnych dziedziczyłaby atrybuty i metody klasy bazowej, a także dodawała własne specyficzne dla danego typu produktu.
Jednakże, w przypadku gdy dodajemy nowe rodzaje produktów, musielibyśmy tworzyć nowe klasy dziedziczące po klasie bazowej, co może prowadzić do powstania drzewa dziedziczenia, które jest trudne do utrzymania i rozwijania.
Zamiast tego, można zastosować kompozycję, tworząc klasę „Product” zawierającą wszystkie atrybuty wspólne dla wszystkich typów produktów oraz klasę „ProductType”, która zawiera atrybuty i metody specyficzne dla danego typu produktu. Klasa „Product” zawierałaby również referencję do obiektu „ProductType”, który definiuje szczegóły danego typu produktu.
Dzięki takiemu podejściu, łatwiej jest dodawać nowe rodzaje produktów, ponieważ nie trzeba tworzyć nowych klas dziedziczących, a jedynie nowe obiekty klasy „ProductType”. Dodatkowo, możemy łatwo zmienić atrybuty specyficzne dla danego typu produktu, bez wpływu na resztę kodu.
Oto prosty przykład zastosowania kompozycji w PHP:
class Product { public function __construct( private string $name, private string $description, private float $price, private ProductType $type ){ } public function getName(): string { return $this->name; } public function getDescription(): string { return $this->description; } public function getPrice(): float { return $this->price; } public function getType(): ProductType { return $this->type; } } class ProductType { public function __construct( private string $color, private string $size ){ } public function getColor(): string { return $this->color; } public function getSize(): string { return $this->size; } } // Tworzymy obiekt klasy Product $productType = new ProductType("Red", "M"); $product = new Product("T-Shirt", "A nice T-Shirt", 20, $productType); // Wyświetlamy informacje o produkcie echo "Product Name: " . $product->getName() . "\n"; echo "Product Description: " . $product->getDescription() . "\n"; echo "Product Price: " . $product->getPrice() . "\n"; echo "Product Type: " . $product->getType()->getColor() . " " . $product->getType()->getSize() . "\n";
W tym przykładzie klasa „Product” reprezentuje produkt, zawierając jego podstawowe atrybuty, takie jak nazwa, opis, cena oraz referencję do obiektu klasy „ProductType”. Klasa „ProductType” reprezentuje typ produktu, zawierając jego specyficzne atrybuty, takie jak kolor i rozmiar.
Tworzymy obiekt klasy „ProductType” z atrybutami „Red” i „M”, a następnie tworzymy obiekt klasy „Product” z atrybutami „T-Shirt”, „A nice T-Shirt”, 20 oraz referencją do obiektu klasy „ProductType”. W ten sposób tworzymy produkt, który jest powiązany z określonym typem produktu.
Następnie wyświetlamy informacje o produkcie, w tym jego nazwę, opis, cenę oraz informacje specyficzne dla danego typu produktu, takie jak kolor i rozmiar.
Dziedziczenie
Oto prosty przykład dziedziczenia w PHP:
class Animal { public function __construct( protected string $name ){ } public function getName(): string { return $this->name; } public function makeSound(): string { return "Unknown sound"; } } class Dog extends Animal { public function makeSound(): string { return "Bark"; } } class Cat extends Animal { public function makeSound() { return "Meow"; } } // Tworzymy obiekty klasy Dog i klasy Cat $dog = new Dog("Buddy"); $cat = new Cat("Fluffy"); // Wyświetlamy imiona zwierząt oraz ich dźwięki echo $dog->getName() . " says " . $dog->makeSound() . "\n"; echo $cat->getName() . " says " . $cat->makeSound() . "\n";
W tym przykładzie klasa „Animal” reprezentuje zwierzę i zawiera atrybut „name” oraz metodę „makeSound()”, która wypisuje nieznany dźwięk. Klasa „Dog” dziedziczy po klasie „Animal” i nadpisuje metodę „makeSound()”, aby wypisywała „Bark”. Klasa „Cat” również dziedziczy po klasie „Animal” i nadpisuje metodę „makeSound()”, aby wypisywała „Meow”.
Tworzymy obiekty klasy „Dog” i klasy „Cat” z określonymi imionami i wywołujemy ich metody „makeSound()” oraz „getName()” w celu wyświetlenia informacji o dźwiękach, jakie wydają oraz ich imionach.
Na koniec, proste zadanie do przećwiczenia. Zamień powyższe klasy Dog i Cat z dziedziczenia na kompozycję.
[…] tym, że w klasie dziedziczącej powinno być możliwe zastąpienie obiektu klasy bazowej obiektem klasy dziedziczącej, bez wpływu na poprawność działania […]