Интерфейсы со статическими полями в java для совместного использования ‘констант’

Я рассматриваю некоторые Java-проекты с открытым исходным кодом, чтобы попасть в Java и заметить, что у многих из них есть своего рода интерфейс констант.

Например, у process.org есть интерфейс под названием PConstants.java , и большинство других основных classов реализуют этот интерфейс. Интерфейс пронизан статическими элементами. Есть ли причина для этого подхода, или это считается плохой практикой? Почему бы не использовать enums, где это имеет смысл , или статический class?

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

public interface PConstants { // LOTS OF static fields... static public final int SHINE = 31; // emissive (by default kept black) static public final int ER = 32; static public final int EG = 33; static public final int EB = 34; // has this vertex been lit yet static public final int BEEN_LIT = 35; static public final int VERTEX_FIELD_COUNT = 36; // renderers known to processing.core static final String P2D = "processing.core.PGraphics2D"; static final String P3D = "processing.core.PGraphics3D"; static final String JAVA2D = "processing.core.PGraphicsJava2D"; static final String OPENGL = "processing.opengl.PGraphicsOpenGL"; static final String PDF = "processing.pdf.PGraphicsPDF"; static final String DXF = "processing.dxf.RawDXF"; // platform IDs for PApplet.platform static final int OTHER = 0; static final int WINDOWS = 1; static final int MACOSX = 2; static final int LINUX = 3; static final String[] platformNames = { "other", "windows", "macosx", "linux" }; // and on and on } 

Обычно это считается плохой практикой. Проблема в том, что константы являются частью публичного «интерфейса» (из-за лучшего слова) реализующего classа. Это означает, что class реализации публикует все эти значения для внешних classов, даже если они необходимы только внутренне. Константы распространяются по всему коду. Примером может служить интерфейс SwingConstants в Swing, который реализуется десятками classов, которые все «реэкспортируют» все свои константы (даже те, которые они не используют) как свои собственные.

Но не просто смирись с этим, Джош Блох также говорит, что это плохо:

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

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

Вместо реализации «интерфейса констант» в Java 1.5+ вы можете использовать статический импорт для импорта констант / статических методов из другого classа / интерфейса:

 import static com.kittens.kittenpolisher.KittenConstants.*; 

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

Что касается практики наличия classа только для хранения констант, я думаю, что это иногда необходимо. Существуют определенные константы, которые просто не имеют естественного места в classе, поэтому лучше иметь их в «нейтральном» месте.

Но вместо использования интерфейса используйте последний class с частным конструктором. (Невозможно создать экземпляр или подclass classа, отправив сильное сообщение о том, что он не содержит нестатические функции / данные.)

Например:

 /** Set of constants needed for Kitten Polisher. */ public final class KittenConstants { private KittenConstants() {} public static final String KITTEN_SOUND = "meow"; public static final double KITTEN_CUTENESS_FACTOR = 1; } 

Я не претендую на право быть правым, но давайте посмотрим на этот небольшой пример:

 public interface CarConstants { static final String ENGINE = "mechanical"; static final String WHEEL = "round"; // ... } public interface ToyotaCar extends CarConstants //, ICar, ... { void produce(); } public interface FordCar extends CarConstants //, ICar, ... { void produce(); } // and this is implementation #1 public class CamryCar implements ToyotaCar { public void produce() { System.out.println("the engine is " + ENGINE ); System.out.println("the wheel is " + WHEEL); } } // and this is implementation #2 public class MustangCar implements FordCar { public void produce() { System.out.println("the engine is " + ENGINE ); System.out.println("the wheel is " + WHEEL); } } 

ToyotaCar ничего не знает о FordCar, и FordCar не знает о ToyotaCar. Принцип CarConstants должен быть изменен, но …

Константы не должны меняться, потому что колесо круглое, а egine – механическое, но … В будущем инженеры-исследователи Toyota изобрели электронный двигатель и плоские колеса! Давайте посмотрим наш новый интерфейс

 public interface InnovativeCarConstants { static final String ENGINE = "electronic"; static final String WHEEL = "flat"; // ... } 

и теперь мы можем изменить нашу абстракцию:

 public interface ToyotaCar extends CarConstants 

в

 public interface ToyotaCar extends InnovativeCarConstants 

И теперь, если нам когда-либо понадобится изменить основное значение, если ДВИГАТЕЛЬ или КОЛЕС можно изменить интерфейс ToyotaCar на уровне абстракции, не касайтесь реализаций

Его НЕ БЕЗОПАСНО, я знаю, но я все еще хочу знать, что вы думаете об этом

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

  1. Концепции являются частью публичного интерфейса нескольких classов.

  2. Их ценности могут измениться в будущих выпусках.

  3. Критически важно, чтобы все реализации использовали одни и те же значения.

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

Таким образом, вы пишете открытый интерфейс со статической константой:

 public interface SyntaxExtensions { // query type String NEAR_TO_QUERY = "nearTo" // params for query String POINT = "coordinate" String DISTANCE_KM = "distanceInKm" } 

Теперь, позже, новый разработчик считает, что ему нужно построить лучший индекс, поэтому он приходит и строит реализацию R *. Внедряя этот интерфейс в свое новое дерево, он гарантирует, что разные индексы будут иметь одинаковый синтаксис в языке запросов. Более того, если позже вы решили, что «nearTo» было запутанным именем, вы можете изменить его на «insideDistanceInKm» и знать, что новый синтаксис будет соблюдаться всеми вашими реализациями индекса.

PS: вдохновение для этого примера взято из пространственного кода Neo4j.

Учитывая преимущество задним числом, мы можем видеть, что Java нарушена многими способами. Одним из основных недостатков Java является ограничение интерфейсов на абстрактные методы и статические конечные поля. Новые, более сложные языки OO, такие как Scala, подразделяют интерфейсы по признакам, которые могут (и обычно) включать конкретные методы, которые могут иметь arity zero (константы!). О экспозиции по чертам как единицам композиционного поведения см. http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf . Для краткого описания того, как характеристики Scala сравниваются с интерфейсами в Java, см. http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5 . В контексте обучения OO-дизайну упрощенные правила, такие как утверждение, что интерфейсы никогда не должны включать статические поля, глупы. Многие черты, естественно, include константы, и эти константы являются надлежащим образом частью открытого «интерфейса», поддерживаемого признаком. При написании кода Java нет чистого и элегантного способа представления признаков, но использование статических конечных полей внутри интерфейсов часто является частью хорошего обходного пути.

Согласно спецификации JVM, поля и методы в интерфейсе могут иметь только Public, Static, Final и Abstract. Ссылка из внутренней виртуальной машины Java

По умолчанию все методы в интерфейсе абстрактны даже жестко, вы не указали это явно.

Интерфейсы предназначены только для спецификации. Он не может содержать никаких реализаций. Поэтому Чтобы избежать реализации classов для изменения спецификации, она становится окончательной. Поскольку интерфейс не может быть создан, они становятся статическими для доступа к полю с использованием имени интерфейса.

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

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

Если клиент хочет сравнить автомобили перед покупкой, у него может быть такой способ:

 public List compareCars(List pCars); 

Интерфейс – это договор о поведении и показывает разные объекты с одной точки зрения. То, как вы его проектируете, будет у каждого автомобильного бренда собственной линией наследования. Хотя это на самом деле совершенно правильно, потому что автомобили могут быть такими разными, что это может быть похоже на сравнение совершенно разных типов объектов, в конце концов выбор между разными автомобилями. И это перспектива интерфейса, которым все бренды должны делиться. Выбор констант не должен делать это невозможным. Пожалуйста, рассмотрите ответ Зарконнен.

Это произошло с момента, когда Java 1.5 существует и приносит нам enums. До этого не было хорошего способа определить набор констант или ограниченных значений.

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

  • Размеры динамических массивов Java?
  • Можно ли настроить собственный шрифт для всего приложения?
  • Как установить изображение экрана блокировки Android
  • Stream Live Android Audio на сервер
  • почему MenuItemCompat.getActionProvider возвращает значение null?
  • Как использовать перехватчик для добавления заголовков в «Дооснащение 2.0»
  • Android: невозможно найти явный class активности ... startActivity from the PreferenceActivity
  • Отправка электронной почты на Android с использованием API JavaMail без использования стандартного / встроенного приложения
  • Быстрое и простое строковое шифрование / расшифровка в JAVA
  • Как поместить банку в classpath в Eclipse?
  • Может ли int быть пустым в Java?
  • Давайте будем гением компьютера.