Для чого використовують константи інтерфейсу?


123

Я вивчаю Java і щойно з’ясував, що в Інтерфейсі можуть бути поля, які є загальнодоступними і статичними. Я досі не бачив прикладів таких. Назвіть декілька випадків використання цих констант інтерфейсу і чи можу я їх побачити у стандартній бібліотеці Java?

Відповіді:


190

Вміщення статичних членів в інтерфейс (і реалізація цього інтерфейсу) є поганою практикою, і навіть існує його назва, постійний інтерфейс 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
Добре, але що робити, якщо у вас є інтерфейс, який не тільки для постійного визначення. Тож у мене є інтерфейс, який реалізований багатьма класами, він містить декларації методів, але я також хочу додати до нього також деякі загальні значення, наприклад розмір. Чи справді це поганий зразок у цьому випадку. Взагалі я згоден, що створення інтерфейсів лише для констант є антипатерном.
Łukasz Rzeszotarski

11
Це не погано лише тому, що хтось так говорить у книзі. Поки ви не реалізуєте цей інтерфейс, щоб отримати доступ до цих констант, це нормально.
ACV

11
Ні, ні. Ні. Цитата в "Ефективній Java" - це щось інше! Створення лише інтерфейсів констант є кращою альтернативою класам, що містять ці константи. Ефективна Java говорить: "Те, що клас використовує деякі константи внутрішньо, є деталлю реалізації". Але тут це не так. Ми говоримо про "глобальні" константи. І хто хотів би реалізувати інтерфейс без оголошень методу в ньому?
ACV

6
Я згоден з ACV. Якщо постійний інтерфейс не є частиною експортованого модуля API або коли він ніде не реалізований, я не бачу проблеми. Використання випускних класів const некрасиво: вам потрібен приватний конструктор, який захаращує код, оскільки він не має жодної користі.
Лоуренс

2
Ця відповідь не представляє головного моменту з книги "Ефективна Java" правильно, оскільки вона "понижує" основну точку, ставлячи її в дужки: "(та реалізуючи цей інтерфейс)". Натомість він підкреслює точку констант в інтерфейсах (що нормально, якщо ви не реалізуєте такий інтерфейс). Це взагалі не погана відповідь, тому що якщо ви уважно прочитаєте цитований фрагмент, то зможете побачити, що спочатку мав на увазі автор «Ефективної Java». Але я вважаю це оманливим. На мою думку, частина "та реалізація цього інтерфейсу" повинна бути жирним шрифтом, щоб підкреслити його.
КРМ

17

" Постійна схема інтерфейсу - це погане використання інтерфейсів "

Хто б не придумав цю гіпотезу, як би не був гуру, він придумав її на основі необхідності продовжувати ефективно застосовувати шкідливі звички та практики. Гіпотеза базується на просуванні обґрунтованості звичних шкідливих звичок програмного забезпечення.

Я написав тут спростування відповіді на цю гіпотезу: Який найкращий спосіб впровадити константи на Java? пояснення безпідставності цієї гіпотези.

Протягом 10 років це питання залишалося відкритим, поки воно не було закрито протягом 2 годин після того, як я виклав мотиви, що виправдовують цю гіпотезу, тим самим викриваючи НЕБЕЗПЕЧНІСТЬ до дискусій з боку тих, хто дорого тримається цієї помилкової гіпотези.

Це моменти, які я висловив проти гіпотези

  • Основою для проведення цієї гіпотези є необхідність в методах та правилах RESTRICTIVE для подолання наслідків шкідливих програмних звичок та методологій.

  • Прихильники настроїв " Постійна схема інтерфейсу - це погане використання інтерфейсів" не в змозі надати будь-яких причин, крім тих, що були викликані необхідністю впоратися з наслідками цих шкідливих звичок та практики.

  • Вирішіть принципове питання.

  • І тоді, чому б не повною мірою використовувати та не використовувати кожну мовну особливість мовної структури Java для власної зручності. Куртки не потрібні. Навіщо вигадувати правила, які забарикадують ваш неефективний спосіб життя, щоб дискримінувати та інкримінувати більш ефективний спосіб життя?

Фундаментальне питання

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

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

Існують дві чи три суттєві перешкоди для організації інформації.

  1. Відсутність сприйняття необхідності "нормалізації" моделі даних.

  2. Висловлювання EF Codd щодо нормалізації даних є хибними, несправними та неоднозначними.

  3. Останнє примха, що маскується як спритна інженерія - це хибне уявлення про те, що не слід планувати і обумовлювати організацію модулів вперед, тому що ви можете рефакторировать по мірі свого руху. Рефакторинг і постійні зміни, не перешкоджаючи майбутнім відкриттям, використовуються як привід. Тоді істотними виявленнями поведінки інформаційних процесів є використання методів обліку для затримки прибутку та атестації, тому важливі знання та їх обробка зараз вважаються не потрібними.

Використання констант інтерфейсу - хороша практика.

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

Не забороняйте право власності на зброю з тієї причини, що є люди, які або не знають, як поводитися зі зброєю, або схильні до зловживань зі зброєю.

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

Нерозумне міркування - Інтерфейси не були призначені флоундерами мови Java, щоб їх використовувати таким чином?

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

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

Використання констант Enum - це фактично жахлива практика.

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

Тим більше, що вам не рекомендується нормалізувати і складати свої параметри, складеться помилкове враження, що параметри, змішані в сумку Enum, належать до одного і того ж виміру.

Константи є контрактом API

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

Тому інтерфейси є ідеальним способом реалізації договору Константи.

Дивна презумпція - Що робити, якщо інтерфейс ненавмисно реалізується.

Так. Будь-хто міг ненавмисно реалізувати будь-який інтерфейс. Ніщо не стане на шляху таких ненавмисних програмістів.

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

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

Не використовувати IDE - це погана практика

Нормального функціонуючого та ЕФЕКТИВНОГО програміста немає для того, щоб довести, як довго вона може перебувати під водою, наскільки далеко вона могла б ходити у бліскучій спеці чи мокрих грозах. Їй потрібно використовувати такий ефективний інструмент, як автомобіль, автобус або хоча б велосипед, щоб їздити на роботу 10 км щодня.

Не накладайте обмежень на інших програмістів лише тому, що у вас є езотерична аскетичність, одержима програмою без IDE.

Кілька рамок розроблені, щоб допомогти програмістам продовжувати ефективно практикувати шкідливі звички.

OSGI - це така структура. Так само і указ проти констант інтерфейсу.

Тому остаточна відповідь ...

Константи інтерфейсу є ефективним та ефективним способом розміщення в контракті добре розроблених та нормалізованих компонентів моделі даних.

Константи інтерфейсу у відповідному приватному інтерфейсі, вкладені у файл класу, також є хорошою практикою для групування всіх ваших приватних констант, а не розкидання їх по всьому файлу.


29
Усі ваші моменти можна зробити без жарту, сарказму, емоційності. Stackoverflow - це не блог-платформа.
tkruse

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

"Для запису параметра до значення потрібен запис додаткового коду. Той факт, що засновники Java не надали відображення значень параметрів без написання вами того, що код відображення демонструє константи Enum, так само нецільове використання мови Java" Як інакше ви отримаєте відображення значень параметрів без написання додаткового коду?
ГабріельОширо

Використовуйте константи інтерфейсу. ТОЧНО.
Благословенний гек

9

Зараз я натрапив на це старе запитання кілька разів, і прийнята відповідь все ще мене бентежить. Після багато роздумів, я думаю, що це питання можна ще уточнити.

Навіщо використовувати Interface Constant?

Просто порівняйте їх:

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 має неправильний наголос, ось моя версія його:

Вміщення статичних членів в інтерфейс ( і реалізація цього інтерфейсу ) є поганою практикою.

Цитата «Ефективна Java» має припущення про постійний інтерфейс, який реалізують інші, що, на мою думку, не повинно (і не відбудеться).

Коли ви створюєте постійний інтерфейс, який називається щось подібне Constants, його ніхто не повинен реалізовувати. (хоча технічно це можливо, це єдине питання тут)

Це не відбудеться у стандартній бібліотеці

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

Тим НЕ менше , для щоденних проектів нормальних розробників, що використовують інтерфейс констант набагато простіше , тому що вам не потрібно турбуватися про static, final, empty constructorі т.д., і це не викличе якихось - або поганий питання дизайну. Єдиний недолік, про який я можу подумати, - це те, що він все ще має назву "інтерфейс", але не більше того.

Нескінченні дебати

Зрештою, я думаю, що всі лише цитують книги і дають думки та обґрунтування на свої позиції. Не виняток для мене. Можливо, рішення все ж залежить від розробників кожного проекту. Просто продовжуйте використовувати його, якщо ви відчуваєте себе комфортно. Найкраще, що ми можемо зробити, це зробити його послідовним протягом усього проекту .


"Введення статичних членів в інтерфейс (і реалізація цього інтерфейсу) - це погана практика." Ні, це не так
дощ

Модифікатор publicтакож може бути опущений, оскільки це інтерфейс, що робить його просто double PI = 3.14159;Використання Constants.PIне потребує класу, використовуючи це для реалізації Constantsінтерфейсу! Я думаю, що інтерфейсний підхід набагато чистіший з точки зору використання, IMHO
krozaine

@krozaine Оновлено Дякуємо за нагадування. Довідка для тих, хто сумнівається: "Усі постійні значення, визначені в інтерфейсі, неявно є загальнодоступними, статичними та кінцевими." - docs.oracle.com/javase/tutorial/java/IandI/interfaceDef.html
Nakamura

6

Джошуа Блох, "Ефективна Java - Посібник з мов програмування":

Постійна схема інтерфейсу - це погане використання інтерфейсів. Те, що клас використовує деякі константи внутрішньо, є деталлю реалізації. Реалізація постійного інтерфейсу змушує цю деталь реалізації просочитися до експортованого API класу. Для користувачів класу не має жодних наслідків, що клас реалізує постійний інтерфейс. Насправді це може навіть збити з пантелику. Гірше, що це означає зобов'язання: якщо в майбутньому випуску клас буде змінено так, що йому більше не потрібно використовувати константи, він все одно повинен реалізувати інтерфейс для забезпечення бінарної сумісності. Якщо нефінальний клас реалізує постійний інтерфейс, усі його підкласи матимуть свої простори імен, забруднені константами в інтерфейсі.


Ви додали буквально нічого цінного, про що вже не було сказано.
Стипендіати Доналу

3

Вони корисні, якщо у вас є загальні константи, які будуть використовуватися в класах, що реалізують інтерфейс.

Ось приклад: http://www.javapractices.com/topic/TopicAction.do?Id=32

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


2

Є відповіді, які є дуже резонансними.

Але у мене є деякі думки з цього питання. (може бути неправильним)

На мою думку, поля в інтерфейсі не повинні бути константами всього проекту, вони є лише засобом для інтерфейсу, а інтерфейси розширюють його та класи, які реалізують ці інтерфейси або мають тісний зв’язок з ними. Вони повинні використовуватися в певному діапазоні, не глобальному.


Вони дійсно повинні використовуватися в межах цих класів впровадження.
Дощ

2

Два пункти щодо інтерфейсу:

  • Інтерфейс описує підмножину того, що може робити об’єкт, який реалізує його. (Це інтуїція)

  • Інтерфейс описує звичайні константи, за якими слідують об'єкти, які його реалізують.

    • Ці загальні константи призначені, щоб клієнт знав більше про ці об'єкти.
    • Отже, Інтерфейс констант дійсно є протиінтуїтивним для визначення глобальних констант , оскільки інтерфейс використовується для опису деяких об'єктів , а не всіх об'єктів / жодного об'єкта (врахуйте значення глобального ).

Тому я думаю, що якщо константи не використовуються для глобальних констант , то це прийнятно:

  • Якщо ці загальні константи мають хороші імена, вони рекомендують інспектору використовувати їх (Дивіться мій перший пункт для прикладу нижче)
  • Якщо клас хоче синхронізуватися з тими класами, що відповідають специфікації, просто 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 ) {
        ...
    }

}

У цьому прикладі:

  • Звідси Circle implements Drawableви відразу знаєте, що Circle, ймовірно, відповідає константам, визначеним у Drawable, інакше їм доведеться вибрати гірше ім’я, оскільки хороші PIі GOLDEN_RATIOвже прийняті!
  • Тільки ці Drawableоб'єкти відповідають конкретному PIта GOLDEN_RATIOвизначеному в Drawable, можуть бути об'єкти, які не Drawableмають різної точності співвідношення пі та золота.

Я неправильно зрозумів вашу відповідь чи ви неправильно розумієте мету інтерфейсу? Інтерфейс НЕ є підмножиною. Інтерфейс - це договір, який залежно від договору повинен забезпечити взаємодію, визначену цим договором. Єдиним правильним використанням інтерфейсу є його використання для декларації / виконання договору.
Благословенний

@BlessedGeek Ви виконуєте контракт, тоді можливість, описана контрактом, є підмножиною того, що ви можете зробити.
Дощ

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: Я підтримав ваш коментар, але тепер у мене є гарне пояснення вашого запитання: "не існує прив'язки, яка змушує споживача вашого API фактично використовувати цю константу", але тепер клієнт більше не може використовувати CONSTANT_NAME, оскільки він був б / в, що є хорошим знаком для мене!
Дощ

Отже, постійна назва недоступна у вашому класі, але це передбачає, що я буду називати константу точно такою ж справою. Використання інтерфейсу для представлення констант все ще є помилкою, інтерфейс - це контракт для 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);

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

1
Це жахливо, тому що ви можете робити точно так само і з класами, отримувати доступ таким же чином, а також робити класи остаточними, а їх конструктори приватними (якщо ви хочете разом просто сумку констант). Крім того, ви уникаєте людей, які розширюють ваш клас. Ви не можете уникнути людей, що реалізують ваш інтерфейс?
ГабріельОширо

1
Чому в класі робити те, що можна робити в інтерфейсі ?? Як я заважаю людям взагалі ненароком впроваджувати будь-який інтерфейс - це питання. Чому ви вибираєте вишні на константах інтерфейсу?
Блаженний

-2

поля повинні бути оголошені в інтерфейсі, щоб їх було легше ділитися і на них можна посилатись, не вводячи додаткового з’єднання.

Джерело: Стиль кодування Java Developer Tools


можливо, перед java6. але це здебільшого не має значення.
tkruse

І зв’язок розірваний. Codepro більше немає.
Стівен C
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.