Какая польза от интерфейсных констант?


123

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

Ответы:


190

Помещение статических членов в интерфейс (и реализация этого интерфейса) - плохая практика, и для него даже есть название - Constant Interface Antipattern , см. Эффективная Java , Правило 17:

Шаблон постоянного интерфейса - это плохое использование интерфейсов . То, что класс использует некоторые константы внутри, является деталью реализации. Реализация постоянного интерфейса приводит к просачиванию этой детали реализации в экспортируемый API класса. Для пользователей класса не имеет значения, что класс реализует постоянный интерфейс. На самом деле, это может даже сбить их с толку. Хуже того, это представляет собой обязательство: если в будущем выпуске класс будет изменен так, что ему больше не нужно будет использовать константы, он все равно должен реализовать интерфейс, чтобы гарантировать двоичную совместимость. Если нефинальный класс реализует постоянный интерфейс, пространство имен всех его подклассов будет загрязнено константами в интерфейсе.

В библиотеках платформы Java есть несколько постоянных интерфейсов, например java.io.ObjectStreamConstants. Эти интерфейсы следует рассматривать как аномалии и не должны имитироваться.

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

public final class Constants {

    private Constants() {
        // restrict instantiation
    }

    public static final double PI = 3.14159;
    public static final double PLANCK_CONSTANT = 6.62606896e-34;
}

И для доступа к константам без необходимости их полной квалификации (т.е. без необходимости указывать перед ними имя класса) используйте статический импорт (начиная с Java 5):

import static Constants.PLANCK_CONSTANT;
import static Constants.PI;

public class Calculations {

    public double getReducedPlanckConstant() {
        return PLANCK_CONSTANT / (2 * PI);
    }
}

12
Хорошо, но что, если у вас есть интерфейс, который предназначен не только для постоянного определения. Итак, у меня есть интерфейс, который реализуется многими классами, он содержит объявления методов, но я также хочу добавить туда некоторые общие значения, например, размер. Неужели это плохой узор в данном случае. В целом я согласен, что создание интерфейсов только для констант - это антипаттерн.
Лукаш Жешотарский

11
Это неплохо только потому, что так сказано в книге. Пока вы не реализуете этот интерфейс для доступа к этим константам, все в порядке.
ACV

11
Нет Нет Нет. Эта цитата в Effective Java о другом! Создание интерфейсов только с константами - лучшая альтернатива классам, содержащим эти константы. Эффективная Java гласит: «То, что класс использует некоторые константы внутри, - это деталь реализации». Но здесь это не так. Мы говорим о «глобальных» константах. И кто захочет реализовать интерфейс без объявления методов в нем?
ACV

6
Я согласен с ACV. Если постоянный интерфейс не является частью экспортированного модуля API или когда он нигде не реализован, я не вижу проблемы. Использование классов const final некрасиво: вам нужен частный конструктор, который загромождает код, поскольку он бесполезен.
Лоуренс

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

17

« Постоянный шаблон интерфейса - плохое использование интерфейсов »

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

Я написал здесь ответ, опровергающий эту гипотезу: Как лучше всего реализовать константы в Java? объясняя безосновательность этой гипотезы.

В течение 10 лет этот вопрос оставался открытым, пока он не был закрыт в течение 2 часов после того, как я опубликовал свои доводы, опровергающие эту гипотезу, тем самым выставив НЕЖЕЛАНИЕ для обсуждения теми, кто придерживается этой ошибочной гипотезы.

Это те моменты, которые я высказал против гипотезы

  • Основанием для поддержки этой гипотезы является необходимость в методах и ОГРАНИЧИТЕЛЬНЫХ правилах, чтобы справиться с последствиями плохих программных привычек и методологий.

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

  • Решите основную проблему.

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

Основная проблема

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

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

Есть два или три существенных препятствия на пути к организации информации.

  1. Отсутствие восприятия необходимости «нормализации» модели данных.

  2. Заявления EF Codd о нормализации данных ошибочны, неполноценны и неоднозначны.

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

Использование констант интерфейса - хорошая практика.

Не придумывайте правила и не выдвигайте никаких фетв против этого только потому, что вам нравятся ваши специальные привычки в программировании.

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

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

Глупая аргументация - интерфейсы не предназначались камбалами языка Java для использования таким образом?

Меня не волнует, каковы исходные намерения отцов-основателей в отношении Конституции США. Меня не волнуют неписаные некодифицированные намерения. Меня волнует только то, что буквально кодифицировано в писаной Конституции, и как я могу использовать их для эффективного функционирования общества.

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

Использование констант Enum на самом деле ужасная практика.

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

Тем более, что вам не рекомендуется нормализовать и разбивать на компоненты ваши параметры, может возникнуть ложное впечатление, что параметры, смешанные в сумке Enum, принадлежат одному и тому же измерению.

Константы - это API Contract

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

Следовательно, интерфейсы - идеальный способ реализации контракта констант.

Странное предположение - что, если интерфейс случайно будет реализован.

Ага. Кто угодно может непреднамеренно реализовать любой интерфейс. Таким непреднамеренным программистам ничто не помешает.

Разработайте и нормализуйте вашу модель данных против утечки

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

Не использовать IDE - плохая практика

Нормально функционирующий и ЭФФЕКТИВНЫЙ программист не может доказать, как долго он может оставаться под водой, как далеко он может пройти в условиях сильной жары или влажных гроз. Она должна использовать эффективный инструмент, такой как автомобиль, автобус или, по крайней мере, велосипед, чтобы каждый день добираться на работу 10 миль.

Не накладывайте ограничений на коллег-программистов только потому, что вы страдаете эзотерическим аскетизмом и одержимы программированием без IDE.

Пара фреймворков призвана помочь программистам продолжать эффективно практиковать вредные привычки.

OSGI - это такая основа. И то же самое - указ против интерфейсных констант.

Поэтому окончательный ответ ...

Константы интерфейса - это эффективный и действенный способ разместить в Contract хорошо спроектированные и нормализованные компоненты модели данных.

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


30
Все ваши аргументы можно было изложить без шуток, сарказма, эмоциональности. Stackoverflow - это не платформа для ведения блогов.
tkruse

1
«Меня не волнуют первоначальные намерения отцов-основателей в отношении Конституции США. Меня не волнуют неписаные некодифицированные намерения. Меня волнует только то, что буквально кодифицировано в письменной Конституции, и как я могу использовать их для эффективное функционирование общества ». - это не хорошая аллегория?
Blessed Geek

«Это требует написания дополнительного кода для сопоставления параметра со значением. Тот факт, что основатели Java не предусмотрели сопоставление значения параметра без вашего написания, что код сопоставления демонстрирует константы Enum, является таким же непреднамеренным использованием языка Java». Как еще можно получить сопоставление значений параметров без написания дополнительного кода?
GabrielOshiro

Используйте константы интерфейса. ИМЕННО.
Blessed Geek

9

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

Зачем использовать константу интерфейса?

Просто сравните их:

public final class Constants {

    private Constants() {
        // restrict instantiation
    }

    public static final double PI = 3.14159;
    public static final double PLANCK_CONSTANT = 6.62606896e-34;
}

против

public interface Constants {

    double PI = 3.14159;
    double PLANCK_CONSTANT = 6.62606896e-34;
}

То же использование. Гораздо меньше кода.

Плохая практика?

Я думаю, что в ответе @Pascal Thivent неправильный акцент, вот моя версия:

Помещение статических членов в интерфейс ( и реализация этого интерфейса ) - плохая практика.

Цитата из Effective Java предполагает, что постоянный интерфейс реализуется другими, чего, я думаю, не должно (и не будет).

Когда вы создаете постоянный интерфейс, названный чем-то вроде Constants, его никто не должен реализовывать. (хотя технически возможно, и это единственная проблема)

Этого не будет в стандартной библиотеке

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

Тем не менее , для ежедневных проектов нормальных разработчиков, использующих интерфейс констант намного проще , потому что вам не нужно беспокоиться о static, final, empty constructorи т.д., и это не вызовет каких - либо плохой вопрос дизайна. Единственный недостаток, о котором я могу думать, это то, что у него все еще есть название «интерфейс», но не более того.

Бесконечные дебаты

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


«Помещение статических членов в интерфейс (и реализация этого интерфейса) - плохая практика». Нет, это не так
Raining

Модификатор publicтакже может быть опущен, поскольку это интерфейс, что делает его простым. double PI = 3.14159;Использование Constants.PIне требует использования класса для реализации Constantsинтерфейса! Я думаю, что интерфейсный подход намного чище с точки зрения использования, ИМХО
krozaine

@krozaine Обновлено. Спасибо за напоминание. Ссылка для всех, кто сомневается: «Все постоянные значения, определенные в интерфейсе, неявно являются общедоступными, статическими и окончательными». - docs.oracle.com/javase/tutorial/java/IandI/interfaceDef.html
Накамура

6

Джошуа Блох, «Эффективная Java - Руководство по языку программирования»:

Шаблон постоянного интерфейса - это плохое использование интерфейсов. То, что класс использует некоторые константы внутри, является деталью реализации. Реализация постоянного интерфейса приводит к просачиванию этой детали реализации в экспортируемый API класса. Для пользователей класса не имеет значения, что класс реализует постоянный интерфейс. На самом деле, это может даже сбить их с толку. Хуже того, это представляет собой обязательство: если в будущем выпуске класс будет изменен так, что ему больше не нужно будет использовать константы, он все равно должен реализовать интерфейс для обеспечения двоичной совместимости. Если нефинальный класс реализует постоянный интерфейс, пространство имен всех его подклассов будет загрязнено константами в интерфейсе.


1
Вы буквально не добавили ничего ценного, о чем еще не было сказано.
Donal Fellows

3

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

Вот пример: http://www.javapractices.com/topic/TopicAction.do?Id=32

Но учтите, что рекомендуется использовать статический импорт вместо констант в интерфейсах. Вот ссылка: http://www.javapractices.com/topic/TopicAction.do?Id=195


2

Есть очень разумные ответы.

Но у меня есть некоторые мысли по этому поводу. (может ошибаться)

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


Они действительно должны использоваться в этих классах реализации.
Raining

2

Два момента об интерфейсе:

  • Интерфейс описывает подмножество того, что может делать объект, который его реализует. (Это интуиция)

  • Интерфейс описывает общие константы, за которыми следуют объекты, которые его реализуют.

    • Эти общие константы предназначены для того, чтобы клиент знал больше об этих объектах.
    • Таким образом, интерфейс констант действительно противоречит интуиции для определения глобальных констант , поскольку интерфейс используется для описания некоторых объектов , а не всех объектов / ни одного объекта (рассмотрите значение слова global ).

Поэтому я думаю, что если интерфейс констант не используется для глобальных констант , то это приемлемо:

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

Пример:

interface Drawable {

    double GOLDEN_RATIO = 1.618033988;
    double PI = 3.141592653;
    ...

    // methods
    ...
}

public class Circle implements Drawable {
    ...
    public double getCircumference() {
        return 2 * PI * r;
    }
}

void usage() {

    Circle circle = new Circle(radius: 3.0);
    double maxRadius = 5.0;

    if ( circle.getCircumference() < 2 * Circle.PI * maxRadius ) {
        ...
    }

}

В этом примере:

  • From Circle implements Drawable, вы сразу знаете, что Circleэто, вероятно, соответствует константам, определенным в Drawable, в противном случае им придется выбрать худшее имя, чем хорошие, PIи GOLDEN_RATIOуже было взято!
  • Только эти Drawableобъекты соответствуют конкретным PIи GOLDEN_RATIOопределенным в Drawable, могут быть объекты не Drawableс разной точностью пи и золотого сечения.

Я неправильно понял ваш ответ или вы неправильно поняли назначение интерфейса? Интерфейс НЕ должен быть подмножеством. Интерфейс - это контракт, в котором тот, кто подписывается на контракт, должен обеспечивать взаимодействие, определенное этим контрактом. Единственное допустимое использование интерфейса - использовать его для объявления / выполнения контракта.
Blessed Geek

@BlessedGeek Вы выполняете контракт, тогда способность, описанная в контракте, является подмножеством того, что вы можете сделать.
Raining

1

javax.swing.SwingConstantsИнтерфейс является примером , который получил статические поля , которые используются среди колеблющихся классов. Это позволяет вам легко использовать что-то вроде

  • this.add(LINE_START, swingcomponent);
  • this.add(this.LINE_START, swingcomponent); или
  • this.add(SwingComponents.LINE_START, swingcomponent);

Однако в этом интерфейсе нет методов ...


1

Я наткнулся на этот вопрос и подумал, что добавлю кое-что, о чем не упоминалось. В общем, я согласен с ответом Паскаля здесь . Однако я не думаю, что константы в интерфейсе «всегда» антипаттерны.

Например, если константы, которые вы определяете, являются частью контракта для этого интерфейса, я думаю, что интерфейс - отличное место для констант. В некоторых случаях просто нецелесообразно проводить частную проверку ваших параметров, не раскрывая контракт пользователям вашей реализации. Без публичного контракта пользователи могут только догадываться, с чем вы выполняете проверку, за исключением декомпиляции класса и чтения вашего кода.

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


1
Проблема с интерфейсами и константами в том виде, как вы описываете, вы можете добавить константу к интерфейсу, но нет привязки, которая заставляет потребителя вашего API фактически использовать эту константу.
Daniel

@Daniel: Я проголосовал за ваш комментарий, но теперь у меня есть хорошее объяснение вашего вопроса: «нет привязки, которая заставляет потребителя вашего API фактически использовать эту константу», но теперь клиент больше не может использовать CONSTANT_NAME, потому что они были б / у, что для меня хороший знак!
Raining

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

-1

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

public interface TestConstants
{
    String RootLevelConstant1 = "RootLevelConstant1";

    interface SubGroup1
    {
        String SubGroupConstant1 = "SubGroup1Constant1";
        String SubGroupConstant2 = "SubGroup1Constant2";
    }

    interface SubGroup2
    {
        String SubGroupConstant1 = "SubGroup2Constant1";
        String SubGroupConstant2 = "SubGroup2Constant2";
    }
}

Группировка - огромный плюс, особенно с большим набором констант.

Чтобы использовать, вы просто соединяете их вместе:

System.out.println(TestConstants.SubGroup1.SubGroupConstant1);
System.out.println(TestConstants.SubGroup2.SubGroupConstant1);
System.out.println(TestConstants.RootLevelConstant1);

Согласен. Он делает в основном то же самое, что и открытый абстрактный класс с общедоступными статическими конечными полями, но гораздо меньшим количеством слов. Все, что вам нужно, это убедиться, что все разработчики в вашей команде следуют передовой практике и не реализуют эти интерфейсы.
Yahor

1
Это ужасно, потому что вы можете сделать то же самое с классами, получить доступ таким же образом, плюс сделать классы окончательными, а их конструкторы - частными (если вам действительно нужен просто пакет констант вместе). К тому же вы избегаете людей расширять ваш класс. Вы ведь не можете избежать того, чтобы люди реализуют ваш интерфейс?
GabrielOshiro

1
Зачем делать в классе то, что можно делать в интерфейсе ?? Как мне вообще предотвратить непреднамеренную реализацию ЛЮБОГО интерфейса - вот в чем вопрос. Почему вы выбираете константы интерфейса?
Blessed Geek

-2

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

Источник: Java Developer Tools Coding Style


возможно до java6. но сегодня это в основном неактуально.
tkruse

И ссылка не работает. Codepro больше нет.
Стивен С.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.