Это плохая практика программирования - помещать абстрактные классы в один и тот же пакет, содержащий их производные классы?
Предполагая, что 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
Структура пакета служит двум целям, которые иногда расходятся.
- Сделайте так, чтобы люди, изучающие ваши API и код, могли легко найти класс, который делает то, что им нужно. В этом случае вы хотите сгруппировать подобные вещи вместе и использовать имена пакетов, которые делают очевидным для тех, кто просматривает Javadoc, где искать. Это говорит о небольшом количестве пакетов с широким разделением, которые имеют смысл для клиентов API.
- Разрешить классам получать доступ к частным API-интерфейсам пакетов. Это доказывает для многих небольших групп тесно связанных классов.
Крупномасштабная организация, такая как пакеты, не должна зависеть от текущих деталей реализации, поэтому я предпочитаю (1), а не (2).
Глубокие иерархии трудно выучить, поэтому, если добавление элемента имени не облегчает изучение вашего API и не помогает людям отфильтровывать биты, не относящиеся к их текущим задачам, оставьте это без внимания.
Удивительно, но ответ. Это зависит.
Если вы собирались сделать двух потомков, то один файл для каждого (как в трех) имеет смысл. Если бы они были маленькими и / или вы бы всегда использовали оба (например, каталог и файл), то есть прагматичный аргумент для того, чтобы в любом случае держать их всех в одном.
Если вы строите какую-то глубокую иерархию наследования, в которой только конечный потомок имеет полезную функциональность (чем абстрактнее класс, тем меньше он делает), тогда помещать его в отдельный файл бессмысленно.