В разработке Java обычно используется больше подклассов, чем в C # / .NET?

34

Я недавно начал смотреть на разработку Android. Это вернуло меня в мир разработки программного обеспечения на Java. В прошлый раз, когда я работал с Java, я должен признать, что ООП я не понимал почти так же, как (я думаю), что я делаю сейчас.

Поскольку я в основном использовал C # в своей карьере, я заметил поразительную разницу в том, как наследуется Java и C #.

В C # казалось, что наследования можно избежать в большинстве ситуаций. Поставленная задача обычно может быть решена с помощью конкретных классов .NET Framework.

В Java, из того, что я собираю из примеров кода, похоже, что среда Java предоставляет множество интерфейсов или абстрактных классов, которые затем должны быть реализованы / расширены разработчиком.

Это кажется слишком большой разницей, чтобы просто сводиться к стилю. В чем причина этого? Я чувствую, что не буду писать чистый Java-код, пока не пойму этого.

Кроме того, это ограничено только Android SDK или это обще-Java подход к ООП?

Или поставить по-другому,

Что в дизайне этих двух языков (кажется, поощряет) более или менее наследственное использование, чем в другом?

Если языки относятся к наследованию одинаково, и если предположить, что мои наблюдения верны, то это означает, что это связано с дизайном фреймворков / библиотек, а не с языками. Какова будет мотивация для такого дизайна?

MetaFight
источник
1
Это верно. У Java слишком много интерфейсов. Я хотел бы знать, почему это поведение разработчика является общим для определенного языка. Я вижу, что это происходит чаще в проектах Java, чем в любой другой. Для этого должна быть причина, а не просто мнение.
Reactgular
1
@MathewFoscarini это только твое мнение . В проектах Java, в которых я принимал участие, разработчики были склонны избегать наследования, как чума. Здесь нет ничего авторитетного, только мое мнение . Хотите опрос ?
комнат
2
Вам почти никогда не нужно извлекать один класс из другого в Java. Вместо этого вы можете реализовать интерфейсы для достижения полиморфизма и реализовать составные объекты и прокси-вызовы методов для достижения совместной функциональности.
DwB
1
@ThinkingMedia В общем, Java переполнена фанатиками / пуристами OSS. Академические принципы являются главной заботой. Разработчики .Net - это прагматики, озабоченные выполнением работы. Они ценят рабочий код больше, чем самый чистый код.
Энди

Ответы:

31

Это кажется слишком большой разницей, чтобы просто сводиться к стилю. В чем причина этого?

Я понимаю, что это в значительной степени является просто стилевое решение. Ну, возможно, не стиль, а идиомы языка / среды. Разработчики стандартной библиотеки Java следовали одному набору рекомендаций по проектированию, а разработчики .NET - другому (хотя у них была возможность увидеть, как работает подход Java).

В реальных языках очень мало, чтобы поощрять или отговаривать наследование. Только две вещи кажутся мне актуальными:

  1. .NET представили дженерики раньше, еще до того, как было реализовано слишком много неуниверсального кода. Альтернатива - много наследования, чтобы типизировать специализированные вещи.

  2. Большим изменением было то, что .NET поддерживал делегатов. В Java вы застряли с (анонимным) наследованием, чтобы обеспечить базовую функциональность переменных. Это приводит к относительно большой разнице в том, как код разработан, чтобы либо использовать преимущества делегатов, либо избегать неуклюжих структур наследования, необходимых для этого в Java.

Telastyn
источник
3
Я думаю, что этот ответ предлагает очень правдоподобное объяснение. Особенно вопрос об анонимном наследовании вместо делегатов. Я наблюдал это много. Спасибо.
MetaFight
3
Не забудьте анонимные типы и лямбда-деревья синтаксиса / выражений, которые были представлены в .NET 3.5. И, конечно , выбор в динамической типизации в .NET 4. Это почти бесспорно , смешанная парадигма языка сейчас, а не строго объектно-ориентированным.
Aaronaught
да, по моему опыту, используя оба (в том числе в смешанных проектах, где одни и те же люди используют оба языка), люди предпочитают использовать большее количество меньших классов при кодировании Java, меньшее количество больших классов (склоняющихся к классам бога), когда используя C #. Умышленно создавая прототипы одной и той же вещи с использованием одного и того же стиля в обоих случаях, вы получаете одинаковое количество классов (при использовании частичных классов вы, скорее всего, получите больше файлов кода даже при использовании C #).
jwenting
6

Это очень разные языки, особенно в этой области. Допустим, у вас есть класс. В этом случае мы сделаем это пользовательским элементом управления, что-то вроде текстового поля. Назовите это UIControl. Теперь мы хотим поместить это в другой класс. В этом случае, поскольку мы используем пользовательский интерфейс для нашего примера, мы назовем его классом CleverPanel. Наш экземпляр CleverPanel захочет узнать о том, что происходит с его экземпляром UIControl по разным причинам. Как это сделать?

В C # основной подход заключается в проверке различных событий, настройке методов, которые будут выполняться при запуске каждого интересного события. В Java, в которой отсутствуют события, обычным решением является передача объекта с различными методами обработки «события» в метод UIControl:

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

Пока что разница между C # и Java невелика. Однако у нас есть интерфейс DoSomething, который не нужен в C #. Кроме того, этот интерфейс может включать в себя множество методов, которые не нужны большую часть времени. В C # мы просто не обрабатываем это событие. В Java мы создаем класс, который обеспечивает нулевую реализацию для всех методов интерфейса, DoSomethingAdapter. Теперь мы заменяем DoSomething на DoSomethingAdapter, и нам вообще не нужно писать никаких методов для чистой компиляции. В итоге мы просто переопределяем методы, необходимые для правильной работы программы. Таким образом, в итоге нам нужен интерфейс и использование наследования в Java, чтобы соответствовать тому, что мы делали с событиями в C #.

Это пример, а не всестороннее обсуждение, но он дает основы того, почему в Java так много наследования, в отличие от C #.

Теперь, почему Java работает таким образом? Гибкость. Объект, переданный на whenSomethingHappens, мог быть полностью передан CleverPanel из другого места. Это может быть что-то, что несколько экземпляров CleverPanel должны передать своим UIControl-подобным объектам, чтобы помочь объекту CleverWindow где-нибудь. Или UIControl может передать его одному из своих компонентов.

Кроме того, вместо адаптера может быть где-то реализация DoSomething, за которой стоят тысячи строк кода. Мы могли бы создать новый экземпляр этого и передать его. Возможно, нам придется переопределить один метод. Обычная хитрость в Java - иметь большой класс с таким методом:

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

Затем в CleverlPanel:

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

Платформа Java с открытым исходным кодом делает многое из этого, что побуждает программистов делать больше - и потому, что они следуют этому в качестве примера, и просто для того, чтобы использовать его. Я действительно думаю, что базовый дизайн языка лежит в основе разработки фреймворка Sun и позади использования Java-программистом техник, когда фреймворк не используется.

Создать класс на лету очень просто. На класс, анонимный или именованный, нужно ссылаться только в одном небольшом блоке кода, скрытом глубоко в одном методе. Он может быть создан совершенно новым или путем небольших модификаций очень большого существующего класса. (И существующий класс может быть верхнего уровня в своем собственном файле, или вложенным в класс верхнего уровня, или определяться только в пределах одного блока кода). Новый экземпляр класса может иметь полный доступ ко всем данным создаваемого объекта. И новый экземпляр можно передать и использовать по всей программе, представляя объект, который его создал.

(Кроме того, обратите внимание, что широкое использование наследования здесь - как и в других местах в Java - просто для целей СУХОЙ. Это позволяет различным классам повторно использовать один и тот же код. Обратите внимание также на простоту наследования в Java, которая поощряет это. )

Опять же, это не всестороннее обсуждение; Я просто царапаю поверхность здесь. Но да, есть удивительная разница в том, как наследование используется между Java и C #. В этом отношении они очень разные языки. Это не ваше воображение.

RalphChapin
источник
Примечание. Вы можете избежать необходимости предоставлять методы, которые ничего не делают, расширяя абстрактный класс, полный пустых реализаций. Таким образом, вы можете выборочно переопределить методы, которые что-то делают. Хотя это и не так некрасиво, это означает еще один уровень наследования.
Питер Лори
-2

Нет абсолютно никакой разницы в способах наследования между Java и C #. Когда вы на самом деле будете использовать наследование или композицию, это определенно дизайнерское решение, и ни в коем случае это не то, что Java или C # поощряет или не поощряет. Я хотел бы предложить прочитать эту статью .

Надеюсь, я помог!

Пантелис Нациавас
источник
5
Вы говорите о самих языках, но я думаю, что вопрос также о библиотеках и обычном использовании.
svick
3
Но есть причуды в остальной части языка , которые стимулируют дополнительное наследование в Java; см. ответ
Ральфа Чапина