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



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



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



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



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

}
341   8  

8 ответов:

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

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

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

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

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

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

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

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

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

например:

/** 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. концепции являются частью публичного интерфейса нескольких занятия.

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

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

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

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

public interface SyntaxExtensions {
     // query type
     String NEAR_TO_QUERY = "nearTo"

     // params for query
     String POINT = "coordinate"
     String DISTANCE_KM = "distanceInKm"
}

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

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

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

согласно спецификации JVM, поля и методы в интерфейсе могут иметь только публичные, статические, окончательные и абстрактные. Ref изнутри Java VM

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

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

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

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

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

public List<Decision> compareCars(List<I_Somecar> pCars);

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

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

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

Comments

    Ничего не найдено.