Według Herb Suttera należy preferować abstrakcyjne interfejsy (wszystkie czyste funkcje wirtualne) niż abstrakcyjne klasy w C ++, aby oddzielić implementację tak dalece, jak to możliwe. Chociaż osobiście uważam tę zasadę za bardzo przydatną, ostatnio dołączyłem do zespołu z wieloma programistami Java i w kodzie Java ta wytyczna wydaje się nie istnieć. Funkcje i ich implementacje bardzo często znajdują się w klasach abstrakcyjnych. Więc źle zrozumiałem Herb Sutter, nawet w przypadku C ++, czy też istnieje ogólna różnica w korzystaniu z funkcji abstrakcyjnych w C ++ w porównaniu z Javą. Czy klasy abstrakcyjne z kodem implementacyjnym są bardziej sensowne w Javie niż w C ++, a jeśli tak, to dlaczego?
java
c++
interfaces
abstract-class
Jaskółka oknówka
źródło
źródło
Odpowiedzi:
OOP ma skład i substytucję.
C ++ ma wiele elementów dziedziczenia, specjalizacji szablonów, osadzania i semantyki value / move / pointer.
Java ma pojedyncze dziedziczenie i interfejsy, osadzanie i semantykę odniesienia.
Powszechnym sposobem, w jaki szkoła OOP używa tych języków, jest stosowanie dziedziczenia do podstawiania obiektów i osadzania do kompozycji. Ale potrzebujesz także wspólnego przodka i sposobu na wykonanie w środowisku uruchomieniowym (w C ++ nazywa się
dynamic_cast
, w Javie po prostu pyta interfejsu od innego).Java robi to wszystko przez swoją własną
java.lang.Object
zakorzenioną hierarchię. C ++ nie ma predefiniowanego wspólnego katalogu głównego, więc powinieneś przynajmniej go zdefiniować, aby uzyskać ten sam „obraz” (ale to ogranicza niektóre możliwości C ++ ...).Następnie możliwość posiadania polimorfizmu w czasie kompilacji (pomyśl o CRTP) i semantycznej wartości może zaoferować także inne alternatywy dla sposobu, w jaki koncepcja „obiektu OOP” może zostać przeniesiona do programu C ++.
Możesz nawet wyobrazić sobie, że herezja używa osadzania i niejawnej konwersji do zarządzania substytucją i prywatnym dziedziczeniem do zarządzania kompozycją, w rzeczywistości odwracając tradycyjny paradygmat szkolny. (Oczywiście w ten sposób jest o 20 lat młodszy od drugiego, więc nie oczekuj wsparcia ze strony szerokiej społeczności)
Lub możesz sobie wyobrazić wirtualną wspólną bazę dla wszystkich klas, od interfejsu interfejsu (bez implementacji) do klas końcowych (w pełni zaimplementowanych) przechodzących przez częściowo zaimplementowane interfejsy, a nawet klastry interfejsów, wykorzystując „dominację” jako wysyłanie z interfejsu do implementacji za pomocą „wielu stosów” -parallelogram ”schemat dziedziczenia.
Porównanie OOP do java z C ++ przy założeniu, że istnieje tylko jeden sposób OOP, ogranicza możliwości obu języków.
Zmuszanie C ++ do ścisłego przestrzegania idiomów kodujących Java oznacza denaturację C ++, ponieważ zmuszanie Javy do zachowywania się jak język C ++ denaturuje Javę.
Nie chodzi o „wrażliwość”, ale o różne „mechanizmy agregacji” dwóch języków i inny sposób ich łączenia, co sprawia, że niektóre idiomy są bardziej opłacalne w jednym języku niż w drugim i odwrotnie.
źródło
Zasada obowiązuje dla obu języków, ale nie robisz uczciwego porównania. Powinieneś porównać klasy czysto abstrakcyjne C ++ z interfejsami Java.
Nawet w C ++ możesz mieć klasy abstrakcyjne, które mają zaimplementowane niektóre funkcje, ale wywodzą się z czystej klasy abstrakcyjnej (bez implementacji). W Javie masz te same abstrakcyjne klasy (z niektórymi implementacjami), które mogą pochodzić z interfejsów (bez implementacji).
źródło
Zasadniczo te same zasady OO obowiązują w Javie i C ++. Jednak jedną wielką różnicą jest to, że C ++ obsługuje wielokrotne dziedziczenie, podczas gdy w Javie można dziedziczyć tylko z jednej klasy. To jest główny powód, dla którego Java ma interfejsy, które moim zdaniem uzupełniają brak wielokrotnego dziedziczenia i prawdopodobnie ograniczają to, co można z tym zrobić (ponieważ istnieje duża krytyka nadużywania wielokrotnego dziedziczenia). Zatem prawdopodobnie w umyśle programistów Java istnieje wyraźniejsze rozróżnienie między klasami abstrakcyjnymi a interfejsami. Klasy abstrakcyjne służą do udostępniania i dziedziczenia zachowania, a interfejsy są po prostu używane do dodawania dodatkowej funkcjonalności. Pamiętaj, że w Javie możesz dziedziczyć tylko jedną klasę, ale możesz mieć wiele interfejsów. Jednak w C ++ czysto abstrakcyjne klasy (tj. „Interfejs C ++”) to używane do dzielenia się i dziedziczenia zachowania w przeciwieństwie do celu interfejsu Java (chociaż nadal musisz implementować funkcje), dlatego użycie różni się od interfejsów Java.
źródło
Czasami sensowne jest posiadanie domyślnej implementacji. Na przykład ogólna metoda PrintError (string msg), która ma zastosowanie do wszystkich podklas.
W razie potrzeby można go w dalszym ciągu zastąpić, ale można zaoszczędzić klientowi kłopotów, pozwalając mu po prostu wywołać wersję ogólną.
źródło