Пояснение к анти-шаблону

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

Ну, я не понимаю необходимости "реализации" в первую очередь. Разве нельзя просто использовать эти статические константы напрямую? Итак, почему я должен пройти через проблему import static когда я могу сделать что-то вроде:

interface Constants {
    public static final int FOO_1 = 1;
    public static final int FOO_2 = 2;
}

public class Test {
    public static void main(String[] args) {
        System.out.println(Constants.FOO_2);
    }
}

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

Ответы

Ответ 1

Я понял... тот факт, что интерфейс может быть реализован отдельным человеком, если это необходимо, оставляет место для проблем, указанных выше (т.е. загрязнения пространства имен, нетрадиционного использования, воздействия через открытый API). Поэтому лучше всего предотвратить возможность реализации интерфейса в целом. Следовательно, более целесообразно иметь класс final с частным конструктором, чтобы он не мог быть создан/расширен.

public final class Constants
{
      // to restrict instantiation
      private Constants() {}

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

... и используйте это в сочетании с import static.

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

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

Ответ 2

Аргументы против "Constant Interface Pattern" в основном стилистические. Вы можете использовать постоянный интерфейс в Java, если он соответствует вашим потребностям, и на самом деле библиотеки Java включают некоторые из них (хотя они считаются плохими примерами, которые не должны повторяться).

Причины, по которым постоянный интерфейс рассматривается многими как "анти-паттерн", перечислены в Effective Java, 2nd Ed. Вкратце, некоторые из причин, по которым такое использование интерфейсов не рекомендуется:

  • Загрязнение пространства имен. Именованные константы появляются в пространстве имен всех реализующих классов, а также их подклассов.

  • Интерфейсы должны определять типы. В Java большинство основных типов в проекте должны быть представлены интерфейсами. Постоянный интерфейс по своей природе не определяет тип.

  • Нестабильные классы со import static. Объявление констант как конечных статических полей в классе (а не в интерфейсе) позволяет достичь тех же целей, что и объявление их в интерфейсе. Это не приводит к загрязнению пространства имен классом. При желании эти константы можно использовать без квалифицирующего имени класса, используя import static объявление import static.

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

Ответ 3

Это не совсем закономерность. Это больше похоже на:

interface Constants {
    final int FOO_1 = 1;
    final int FOO_2 = 2;
}

public class MyClass implements Constants {
    public static void main( String[] args ) {
        System.out.println( FOO_2 ); // compiles OK
    }
}

ИМХО, проблема в том, что MyClass не является Constants. Шаблон использует хитрость видимости, но затуманивает намерение класса. Кроме того, затенение полей может происходить без предупреждений компилятора - это тем более вероятно, потому что вы видите все поля интерфейса, даже если вы не используете их все.

Лучше import static com.foo.Constants.*; для достижения того же удобства кодирования, без неправильного направления членства в Constants.

Ответ 4

Мы можем использовать как класс (финал с приватным конструктором), так и интерфейс.

Однако я бы предпочел занятия по следующим причинам:

  1. Интерфейс и его реализующие классы должны иметь отношение IS A. Например: "Кошка распространяет млекопитающее" означает, что кошка является млекопитающим. Это не выполняется в случае, если мы реализуем константы, которые мы используем в интерфейсах.

  2. Интерфейс должен определять тип.

  3. Однако теперь мы можем использовать статический импорт и с Интерфейсом, но есть риск того, что кто-то реализует его, что нарушит отношение "IS A".

Имейте класс (со следующими свойствами) для констант и сделайте это:

  1. FINAL - поэтому он не может быть унаследован другими классами.
  2. Наличие только частного конструктора: - так что его нельзя создать.

PS: копирование используемого примера

public final class Constants  // so it cannot be inherited
{

  private Constants() {} // to restrict instantiation

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

Теперь использование:

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

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