Czy programowanie w Javie zazwyczaj wymaga więcej podklas niż C # / .NET?

34

Ostatnio zacząłem patrzeć na rozwój Androida. To wróciło do świata tworzenia oprogramowania Java. Muszę przyznać, że kiedy ostatnio pracowałem z Javą, nie rozumiałem OOP tak bardzo, jak (myślę) teraz.

Korzystając głównie z C # w mojej karierze, zauważam zaskakującą różnicę w sposobie używania Java i C #.

W języku C # wydawało się, że w większości sytuacji można uniknąć dziedziczenia. Omawiane zadanie można zwykle wykonać przy użyciu konkretnych klas środowiska .NET .

W Javie, z tego, co zbieram z próbek kodu, wygląda na to, że framework Java dostarcza wiele interfejsów lub klas abstrakcyjnych, które następnie mają zostać zaimplementowane / rozszerzone przez programistę.

Wydaje się, że to zbyt duża różnica, by sprowadzić się do stylu. Jakie jest tego uzasadnienie? Czuję, że nie będę pisać czystego kodu Java, dopóki tego nie zrozumiem.

Ponadto, czy ogranicza się to tylko do zestawu SDK systemu Android, czy jest to podejście OOP obejmujące całą Javę?

Lub w inny sposób,

Co takiego jest w konstrukcji tych dwóch języków, które (jak się wydaje, zachęcają) do mniej lub bardziej dziedziczenia?

Jeśli języki traktują dziedziczenie identycznie i przy założeniu, że moja obserwacja jest prawidłowa, oznacza to, że jest to związane z projektowaniem ram / bibliotek, a nie języków. Jaka byłaby motywacja do tego rodzaju projektu?

MetaFight
źródło
1
To prawda. Java ma zbyt wiele interfejsów. Chciałbym wiedzieć, dlaczego takie zachowanie programistów jest wspólne dla określonego języka. Widzę, że dzieje się to częściej w projektach Java niż w jakimkolwiek innym. Musi być ku temu powód, a nie tylko opinia.
Reactgular
1
@MathewFoscarini to tylko twoja opinia . W projektach Java, w których uczestniczyłem, programiści unikali dziedziczenia jak zarazy. Nic autorytatywnego tutaj, tylko moja opinia . Chcesz sondę ?
komar
2
Prawie nigdy nie musisz czerpać jednej klasy z drugiej w Javie. Zamiast tego można zaimplementować interfejsy w celu osiągnięcia polimorfizmu oraz zaimplementować złożone obiekty i proxy wywołań metod w celu uzyskania funkcjonalnej funkcjonalności.
DwB
1
@ThinkingMedia Ogólnie Java jest przepełniona przez fanatyków / purystów OSS. Zasady akademickie stanowią nadrzędny problem. Programiści .Net są pragmatykami, którzy są zaniepokojeni wykonaniem zadania. Cenią działający kod bardziej niż najczystszy kod.
Andy,

Odpowiedzi:

31

Wydaje się, że to zbyt duża różnica, by sprowadzić się do stylu. Jakie jest tego uzasadnienie?

Rozumiem, że w dużej mierze jest to po prostu decyzja stylistyczna. Cóż, może nie styl, ale idiomy języka / środowiska. Deweloperzy bibliotek standardowych Java stosowali się do jednego zestawu wytycznych projektowych, a programiści .NET - innego (chociaż mieli możliwość zobaczenia, jak działa podejście Javy).

W rzeczywistych językach jest bardzo mało, aby zachęcać lub zniechęcać do dziedziczenia. Tylko dwie rzeczy wydają mi się istotne:

  1. .NET wprowadził generyczne wcześniej, zanim pojawiło się zbyt wiele kodu nieogólnego. Alternatywą jest spore dziedziczenie po wpisaniu specjalizacji.

  2. Większą zmianą było to, że delegaci obsługiwani przez .NET. W Javie utkniesz z (anonimowym) dziedziczeniem, aby zapewnić najbardziej podstawową zmienną funkcjonalność. Prowadzi to do stosunkowo dużej różnicy w sposobie zaprojektowania kodu, aby albo wykorzystać delegatów, albo uniknąć niezręcznych struktur dziedziczenia potrzebnych do zrobienia tego w Javie.

Telastyn
źródło
3
Myślę, że ta odpowiedź oferuje bardzo prawdopodobne wyjaśnienie. Zwłaszcza kwestia anonimowego dziedziczenia zamiast delegatów. Często to obserwowałem. Dzięki.
MetaFight,
3
Nie zapomnij o anonimowych typach i drzewach składni / wyrażeń lambda, które zostały wprowadzone w .NET 3.5. I oczywiście opt-in dynamiczne pisanie w .NET 4. Jest to obecnie niemal bezsprzecznie język o mieszanym paradygmacie, nie jest ściśle obiektowy.
Aaronaught
tak, z mojego doświadczenia wynika, że ​​korzystałem z obu (w tym przy mieszanych projektach, w których ci sami ludzie używają obu języków) ludzie wolą używać większej liczby mniejszych klas podczas kodowania Javy, mniejszej liczby większych klas (tendencja do klas boskich), gdy za pomocą C #. Po celowym prototypowaniu tej samej rzeczy przy użyciu tego samego stylu w obu przypadkach, otrzymujesz podobną liczbę klas (używając klas częściowych, prawdopodobnie uzyskasz więcej plików kodu nawet przy użyciu C #).
jwenting
6

Są to bardzo różne języki, szczególnie w tej dziedzinie. Powiedzmy, że masz klasę. W tym przypadku sprawimy, że będzie to kontrola użytkownika, coś w rodzaju pola tekstowego. Nazwij to UIControl. Teraz chcemy umieścić to w innej klasie. W tym przypadku, ponieważ w naszym przykładzie używamy interfejsu użytkownika, nazwiemy go klasą CleverPanel. Nasza instancja CleverPanel będzie chciała wiedzieć o różnych wydarzeniach związanych z instancją UIControl. Jak to zrobić?

W języku C # podstawowym podejściem jest sprawdzanie różnych zdarzeń, konfigurowanie metod, które będą wykonywane po uruchomieniu każdego interesującego zdarzenia. W Javie, w której brakuje zdarzeń, typowym rozwiązaniem jest przekazanie obiektu z różnymi metodami obsługi „zdarzeń” do metody UIControl:

boolean  stillNeedIt =  ... ;
uiControl.whenSomethingHappens( new DoSomething()  {
    public void resized( Rectangle r )  { ... }
    public boolean canICloseNow()  { return !stillNeedIt; }
    public void closed()  { ... }
    ...
} );

Jak dotąd różnica między C # a Javą nie jest głęboka. Mamy jednak interfejs DoSomething, który nie jest potrzebny w języku C #. Ponadto ten interfejs może zawierać wiele metod, które przez większość czasu nie są potrzebne. W języku C # po prostu nie obsługujemy tego zdarzenia. W Javie tworzymy klasę, która zapewnia zerową implementację dla wszystkich metod interfejsu, DoSomethingAdapter. Teraz zamieniamy DoSomething na DoSomethingAdapter i nie musimy pisać żadnych metod dla czystego kompilacji. W rezultacie po prostu przesłoniliśmy metody, których potrzebujemy, aby program działał poprawnie. W rezultacie potrzebujemy interfejsu i używamy dziedziczenia w Javie, aby dopasować to, co zrobiliśmy ze zdarzeniami w języku C #.

To jest przykład, nie wyczerpująca dyskusja, ale daje podstawy, dlaczego w Javie jest tyle dziedziczenia w przeciwieństwie do C #.

Dlaczego Java działa w ten sposób? Elastyczność. Obiekt przekazany do whenSomethingHappens mógł zostać przekazany do CleverPanel z innego miejsca całkowicie. Może to być coś, co kilka instancji CleverPanel powinno przekazać swoim obiektom podobnym do UIControl, aby wspomóc gdzieś obiekt CleverWindow. Lub UIControl może przekazać go jednemu ze swoich komponentów.

Ponadto zamiast adaptera może istnieć gdzieś implementacja DoSomething, która ma za sobą tysiące linii kodu. Możemy stworzyć nową instancję tego i przekazać ją. Może być konieczne zastąpienie jednej metody. Częstą sztuczką w Javie jest posiadanie dużej klasy za pomocą metody takiej jak:

public class BigClass implements DoSomething  {
    ...many long methods...
    protected int getDiameter()  { return 5; }
}

Następnie w CleverlPanel:

uiControl.whenSomethingHappens( new BigClass()  {
    @Override
    public int getDiameter()  { return UIPanel.currentDiameter; }
} );

Platforma Java typu open source robi wiele z tego, co zmusza programistów do robienia więcej - zarówno dlatego, że podążają za tym przykładem, jak i po prostu, aby go użyć. Wydaje mi się, że podstawową konstrukcją tego języka jest projektowanie frameworka firmy Sun, a programista Java korzysta z technik, gdy nie używa frameworka.

Łatwo jest stworzyć klasę w locie w Javie. Do klasy, anonimowej lub nazwanej, należy odwoływać się tylko w jednym małym bloku kodu ukrytym głęboko w jednej metodzie. Można go stworzyć całkowicie nowy lub przez niewielkie modyfikacje bardzo dużej, istniejącej klasy. (A istniejąca klasa może znajdować się na najwyższym poziomie we własnym pliku lub zagnieżdżona w klasie najwyższego poziomu lub zdefiniowana tylko w jednym bloku kodu). Nowa instancja klasy może mieć pełny dostęp do wszystkich danych obiektu tworzącego. Nowe wystąpienie można przekazać i wykorzystać w całym programie, reprezentując obiekt, który go utworzył.

(Nawiasem mówiąc, zauważ, że duże zastosowanie dziedziczenia tutaj - podobnie jak w innych miejscach w Javie - jest po prostu do OSUSZANIA. Pozwala innym klasom na ponowne użycie tego samego kodu. Zwróć także uwagę na łatwość dziedziczenia w Javie, która zachęca do tego. )

Ponownie, nie jest to wyczerpująca dyskusja; Właśnie drapię tutaj powierzchnię. Ale tak, istnieje zaskakująca różnica w sposobie używania dziedziczenia między Javą a C #. Są to pod tym względem bardzo różne języki. To nie twoja wyobraźnia.

RalphChapin
źródło
Uwaga: Możesz uniknąć konieczności dostarczania metod, które niczego nie robią, rozszerzając klasę abstrakcyjną pełną pustych implementacji. W ten sposób możesz selektywnie przesłonić metody, które coś robią. Choć nie jest tak brzydka, oznacza kolejny poziom dziedziczenia.
Peter Lawrey,
-2

Nie ma absolutnie żadnej różnicy w sposobie obsługi dziedziczenia między Javą a C #. Kiedy faktycznie użyjesz dziedziczenia lub kompozycji, jest to zdecydowanie decyzja projektowa i w żadnym wypadku nie jest to coś, co zachęca lub zniechęca Java lub C #. Proponuję przeczytać ten artykuł .

Mam nadzieję, że pomogłem!

Pantelis Natsiavas
źródło
5
Mówisz o samych językach, ale myślę, że pytanie dotyczy również bibliotek i ich powszechnego użycia.
svick
3
Ale w pozostałej części języka występują dziwactwa, które zachęcają do dodatkowego dziedziczenia w Javie; patrz odpowiedź
RalphChapina