Использование вложенных открытых классов для организации констант

21

Я работаю над приложением со многими константами. При последнем обзоре кода выяснилось, что константы слишком разбросаны и должны быть все организованы в один «главный» файл констант. Разногласия о том, как их организовать. Большинство считает, что использование константного имени должно быть достаточно хорошим, но это приведет к коду, который выглядит следующим образом:

public static final String CREDITCARD_ACTION_SUBMITDATA = "6767";
public static final String CREDITCARD_UIFIELDID_CARDHOLDER_NAME = "3959854";
public static final String CREDITCARD_UIFIELDID_EXPIRY_MONTH = "3524";
public static final String CREDITCARD_UIFIELDID_ACCOUNT_ID = "3524";
...
public static final String BANKPAYMENT_UIFIELDID_ACCOUNT_ID = "9987";

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

public class IntegrationSystemConstants
{
    public class CreditCard
    {
        public static final String UI_EXPIRY_MONTH = "3524";
        public static final String UI_ACCOUNT_ID = "3524";
        ...
    }
    public class BankAccount
    {
        public static final String UI_ACCOUNT_ID = "9987";
        ...
    }
}

Эта идея не была хорошо принята, потому что она была «слишком сложной» (я не получил много подробностей о том, почему это может быть слишком сложно). Я думаю, что это создает лучшее разделение между группами связанных констант, а автозаполнение облегчает их поиск. Хотя я никогда не видел, чтобы это было сделано, поэтому мне интересно, является ли это общепринятой практикой или есть более веские причины, по которым этого не следует делать.

FrustratedWithFormsDesigner
источник
1
Мне скорее нравится идея. Я обнаружил, что делаю что-то похожее в C ++, когда хотел получить область видимости и поведение в стиле C #, но с осмысленными значениями (я думаю, что идея возникла потому, что я привык использовать классы case вместо перечислений в Scala). Конечно, это был личный проект для развлечения, а не командная работа.
KChaloux

Ответы:

13

Я не согласен ни с одним из двух предложений.

Константы должны быть в их соответствующих классах , а не в классе всех констант в любой из двух предложенных форм .

Не должно быть классов / интерфейсов только для констант.

Класс CreditCard(не внутренний класс) должен существовать. Этот класс / интерфейс имеет методы относительно кредитных карт, а также констант UI_EXPIRY_MONTHи UI_ACCOUNT_ID.

Должен существовать класс / интерфейс BankAccount(не внутренний класс). Этот класс / интерфейс имеет методы как для банковских счетов, так и для констант UI_ACCOUNT_ID.

Например, в Java API каждый класс / интерфейс имеет свои константы. Они не входят в класс / интерфейс Constants с сотнями констант, либо сгруппированных во внутренние классы, либо нет.

Например, эти константы, относящиеся к наборам результатов, находятся в интерфейсе ResultSet:

static int  CLOSE_CURSORS_AT_COMMIT 
static int  CONCUR_READ_ONLY 
static int  CONCUR_UPDATABLE 
static int  FETCH_FORWARD 
static int  FETCH_REVERSE 
static int  FETCH_UNKNOWN 
static int  HOLD_CURSORS_OVER_COMMIT 
static int  TYPE_FORWARD_ONLY 
static int  TYPE_SCROLL_INSENSITIVE 
static int  TYPE_SCROLL_SENSITIVE 

Этот интерфейс имеет сигнатуры методов относительно наборов результатов, и реализующие классы реализуют это поведение. Они не просто постоянные владельцы.

Эти константы, относящиеся к действиям пользовательского интерфейса, находятся в интерфейсе javax.swing.Action:

static String   ACCELERATOR_KEY 
static String   ACTION_COMMAND_KEY 
static String   DEFAULT 
static String   LONG_DESCRIPTION 
static String   MNEMONIC_KEY 
static String   NAME 
static String   SHORT_DESCRIPTION 
static String   SMALL_ICON 

Реализующие классы имеют поведение относительно действий пользовательского интерфейса, они не являются просто постоянными держателями.

Я знаю, по крайней мере, один интерфейс «константы» ( SwingConstants) без методов, но у него нет сотен констант, только несколько, и все они связаны с направлениями и позициями элементов пользовательского интерфейса:

static int  BOTTOM 
static int  CENTER 
static int  EAST 
static int  HORIZONTAL 
static int  LEADING 
static int  LEFT 
static int  NEXT 
static int  NORTH 
static int  NORTH_EAST 
static int  NORTH_WEST 
static int  PREVIOUS 
static int  RIGHT 
static int  SOUTH 
static int  SOUTH_EAST 
static int  SOUTH_WEST 
static int  TOP 
static int  TRAILING 
static int  VERTICAL 
static int  WEST 

Я думаю, что константы только классы не являются хорошим дизайном ОО.

Тулаинс Кордова
источник
1
Хотя я согласен с тем, что константы только классы в большинстве случаев являются признаком плохого дизайна, существуют законные применения. Например, константа не всегда «принадлежит» определенному классу. Ключи для дополнений Intent в программировании на Android - один из таких конкретных примеров.
curioustechizen
1
+1, но константы пользовательского интерфейса, вероятно, не должны быть в бизнес-моделях, таких как BankAccount. Они принадлежат к некоторому классу пользовательского интерфейса.
Кевин Клайн
10

выяснилось, что константы слишком разбросаны и должны быть все организованы в один «главный» файл констант.

Это 100% неправильное решение проблемы констант, которые «трудно найти». Это фундаментальная ошибка (к сожалению, слишком часто совершаемая программистами) группировать вещи по техническим критериям, таким как постоянные, перечисления или интерфейсы.

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

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

Майкл Боргвардт
источник
Я не знал о потенциальном воздействии на среду разработки, и я думаю, что подход с вложенными классами не сильно поможет в этом, верно?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner: это зависит от того, сколько усилий механизм инкрементной компиляции тратит на моделирование зависимостей.
Майкл Боргвардт
9

Вы правы. Ваше предложение позволяет программисту import static Constants.BankAccount.*исключать и повторять бесчисленные повторения «BANKACCOUNT_». Конкатенация - плохая замена для фактического определения объема. Тем не менее, я не надеюсь, что вы измените культуру команды. У них есть представление о правильной практике, и вряд ли они изменятся, даже если вы покажете им 100 экспертов, которые говорят, что они не правы.

Мне также интересно, почему у вас вообще есть один файл «Константы». Это очень плохая практика, если только эти константы не связаны каким-либо образом и не очень стабильны. Чаще всего это становится своего рода классом кухонной мойки, который постоянно меняется и зависит от всего.

Кевин Клайн
источник
В настоящее время у нас есть один файл констант, который содержит большинство констант идентификатора UI-поля, и пару других файлов констант, которые зависят от подпроекта. Но теперь константы, специфичные для подпроекта, должны использоваться другими связанными подпроектами, так что именно это побудило идею объединить их все в один файл «мастер» констант. Это лучше, чем сейчас, потому что иногда я даже не знаю, какой файл констант будет ссылаться на значение, а кто-то другой создал дубликат константы, потому что не может найти его в файле констант другого подпроекта.
FrustratedWithFormsDesigner
8
Класс констант, который постоянно меняется ... там есть что-то дзен, если вы копаете достаточно сильно.
KChaloux
2
@FrustratedWithFormsDesigner: идентификаторы полей пользовательского интерфейса? Не знаете, где найти значение? Звучит как огромная куча WTF. Вы имеете мое сочувствие.
Кевин Клайн
6

Оба подхода работают, и это важно в конце дня.

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

Яннис
источник
Для достаточно длинного списка констант я бы даже предпочел постоянные интерфейсы, а не их все в одном классе. Опасно можно выбрать неправильный вариант из автозаполнения. Конечно, это зависит от того, как долго это будет продолжаться :)
Горан Йович
1
@GoranJovic Хорошо, каждый анти-шаблон полезен в некотором смысле (или, по крайней мере, удобно), он не стал бы паттерном, если бы не был.
Яннис
1

Одна из проблем с длинным списком констант заключается в поиске «правильной» константы для использования. Неизменно, кто-то добавляет константу в конце строки вместо того, чтобы добавить ее в группу, которой он принадлежал.

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

Во всяком случае, ваше предложение помогает не использовать «неправильную» константу, даже если она может соответствовать конкретной ситуации. Оборачивая константы в репрезентативный класс, он побудит разработчиков задуматься, стоит ли им их использовать. Например, если я потяну CreditCard.SomeConstantдля общего использования, я действительно должен задаться вопросом, почему General.SomeConstantвместо этого нет для этой ситуации.

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


источник
1

Я делаю это время от времени (в C #), особенно если мне нужно запрограммировать / проанализировать хорошо известную и фиксированную структуру XML или что-то похожее вложенное и тяжелое для строк ... например, пользовательский анализатор блоков конфигурации.

Я создаю вложенную структуру классов, соответствующую иерархической структуре XML, с именами классов, соответствующими элементам, содержащим свойство const string «ElementName» и аналогичное свойство, соответствующее каждому атрибуту (если есть).

Это позволяет очень легко программировать против соответствующего XML.

Эд Гастингс
источник