Это плохая практика программирования - помещать абстрактные классы в один и тот же пакет, содержащий их производные классы?

Предполагая, что A, B, C являются производными от AbstractBaseClass и все они являются частью одного и того же Java-проекта, они являются структурами пакетов в форме...

 package com.whatever.###
 AbstractBaseClass.java

 package com.whatever.###.###
 A.java
 B.java
 C.java

... как правило, предпочтительнее структур пакета в форме...

 package com.whatever.###.###
 AbstractBaseClass.java
 A.java
 B.java
 C.java

?

... или мало кому это нужно?

5 ответов

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

com.example.pricing
 \IPricingProvider.java
 \AbstractPriceProvider.java
com.example.pricing.vendorA
   \PricingEngine.java
com.example.pricing.vendorB
   \PricingEngine.java
com.example.pricing.vendorC
   \PricingEngine.java

Затем в моем коде я использовал import подключить двигатель, который я хотел. Как это:

import com.example.pricing.*;
import com.example.pricing.vendorB.*;

IPricingProvider provider = Engine.create();

Для меня преимуществами была возможность иметь сложные и запутанные реализации для каждого поставщика (два были основаны на отдыхе, один - веб-служба, использующая wsimport поэтому было много сгенерированных Java-файлов), и автозаполнение Eclipse не выглядело как настоящий кошмар. Кроме того, стало проще передавать одного поставщика другому разработчику.

Я думаю, это зависит от того, какая из частей является более важной для клиентов: суперкласс или подклассы?

Последнее часто бывает, когда вы думаете сначала о подклассах, а просто превращаете некоторые общие части в суперкласс. Тогда этот суперкласс должен быть в том же пакете, и не быть публично видимым, как сказал Луи в своем комментарии. Если клиенты должны видеть типы подклассов, то вы склонны иметь этот шаблон.

Однако, когда вы абстрагируетесь от реализаций, а клиенты обычно работают только с суперклассом, как в ответе Джейсона, тогда вы должны следовать стратегии помещения каждого подкласса в его собственный пакет. Часто этим подклассам нужны дополнительные классы, которые не имеют отношения к внешнему коду, поэтому для них полезен собственный пакет.

На самом деле это вообще хорошая практика. Слишком много пакетов могут запутаться очень быстро. Хранение классов реализации в одном и том же пакете позволяет избежать лишних операторов импорта и позволяет легко находить ваш абстрактный класс по мере роста кодовой базы приложения. Исключением является ситуация, когда у вас есть большие группы реализующих классов, которые все выходят из абстрактных классов. Так, например, если у вас есть реализация MySQL и реализация Mongo с несколькими абстрактными предложениями, вы можете поместить их в отдельные подпакеты. Что-то вроде

com.foo.data  <--- abstract classes
com.foo.data.mysql  <-- impl
com.foo.data.mongo  <-- impl

Структура пакета служит двум целям, которые иногда расходятся.

  1. Сделайте так, чтобы люди, изучающие ваши API и код, могли легко найти класс, который делает то, что им нужно. В этом случае вы хотите сгруппировать подобные вещи вместе и использовать имена пакетов, которые делают очевидным для тех, кто просматривает Javadoc, где искать. Это говорит о небольшом количестве пакетов с широким разделением, которые имеют смысл для клиентов API.
  2. Разрешить классам получать доступ к частным API-интерфейсам пакетов. Это доказывает для многих небольших групп тесно связанных классов.

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

Глубокие иерархии трудно выучить, поэтому, если добавление элемента имени не облегчает изучение вашего API и не помогает людям отфильтровывать биты, не относящиеся к их текущим задачам, оставьте это без внимания.

Удивительно, но ответ. Это зависит.

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

Если вы строите какую-то глубокую иерархию наследования, в которой только конечный потомок имеет полезную функциональность (чем абстрактнее класс, тем меньше он делает), тогда помещать его в отдельный файл бессмысленно.

Другие вопросы по тегам