Zawsze podobał mi się pomysł obsługiwania wielokrotnego dziedziczenia w jednym języku. Najczęściej jednak jest celowo zapominany, a domniemanym „zamiennikiem” są interfejsy. Interfejsy po prostu nie obejmują tego samego gruntu, co wielokrotne dziedziczenie, a to ograniczenie może czasami prowadzić do większej liczby kodów wzorcowych.
Jedynym podstawowym powodem, dla którego kiedykolwiek to słyszałem, jest problem z diamentami w klasach podstawowych. Po prostu nie mogę tego zaakceptować. Dla mnie wychodzi to okropnie: „Cóż, można to zepsuć, więc automatycznie jest to zły pomysł”. Możesz jednak popsuć wszystko w języku programowania i mam na myśli wszystko. Po prostu nie mogę tego potraktować poważnie, przynajmniej nie bez dokładniejszego wyjaśnienia.
Świadomość tego problemu to 90% bitwy. Co więcej, wydaje mi się, że już dawno temu słyszałem o obejściu ogólnego zastosowania obejmującym algorytm „kopertowy” lub coś w tym rodzaju (czy to ktoś dzwoni, ktoś?).
Jeśli chodzi o problem z diamentem, jedynym potencjalnie autentycznym problemem, jaki mogę wymyślić, jest próba użycia biblioteki innej firmy i nie widzę, że dwie pozornie niezwiązane klasy w tej bibliotece mają wspólną klasę podstawową, ale oprócz dokumentacja, prosta funkcja językowa może wymagać, powiedzmy, wyraźnego zadeklarowania zamiaru stworzenia diamentu, zanim faktycznie go skompiluje. Dzięki takiej funkcji każde stworzenie diamentu jest celowe, lekkomyślne lub dlatego, że nie jest się świadomym tej pułapki.
Tak więc wszystko zostało powiedziane ... Czy jest jakiś prawdziwy powód, dla którego większość ludzi nienawidzi wielokrotnego dziedziczenia, czy może to tylko histeria, która powoduje więcej szkody niż pożytku? Czy jest coś, czego tu nie widzę? Dziękuję Ci.
Przykład
Samochód rozszerza WheeledVehicle, KIASpectra rozszerza samochód i elektronikę, KIASpectra zawiera radio. Dlaczego KIASpectra nie zawiera elektroniki?
Ponieważ jest to elektronika. Dziedziczenie vs. składanie zawsze powinno być relacją typu „jest relacją a relacją typu relacja”.
Ponieważ jest to elektronika. Przewody, płytki drukowane, przełączniki itp. To wszystko w górę iw dół.
Ponieważ jest to elektronika. Jeśli akumulator zużyje się w zimie, masz tyle samo kłopotów, jakby nagle wszystkie koła zniknęły.
Dlaczego nie skorzystać z interfejsów? Weźmy na przykład # 3. Nie chcę pisać tego od nowa i naprawdę nie chcę tworzyć dziwacznych klas pomocników proxy, aby to zrobić:
private void runOrDont()
{
if (this.battery)
{
if (this.battery.working && this.switchedOn)
{
this.run();
return;
}
}
this.dontRun();
}
(Nie zastanawiamy się, czy ta implementacja jest dobra czy zła.) Możesz sobie wyobrazić, jak może być kilka z tych funkcji związanych z elektroniką, które nie są powiązane z niczym w WheeledVehicle i odwrotnie.
Nie byłem pewien, czy zdecydować się na ten przykład, czy nie, ponieważ jest tam miejsce na interpretację. Możesz również pomyśleć w kategoriach samolotu rozszerzającego pojazd i FlyingObject i ptaka rozszerzającego zwierzę i FlyingObject, lub w kategoriach znacznie czystszego przykładu.
Traits
- działają one jak interfejsy z opcjonalną implementacją, ale mają pewne ograniczenia, które pomagają zapobiegać problemom takim jak problem z diamentem.
KiaSpectra
nie jest Electronic
; to jest elektronika, a może być ElectronicCar
(co byłoby rozszerzyć Car
...)