Постоянный интерфейс
![]() |
В языке программирования Java шаблон интерфейса констант классы описывает использование интерфейса исключительно для определения констант, а реализуют этот интерфейс для достижения удобного синтаксического доступа к этим константам. Однако, поскольку константы очень часто являются просто деталями реализации, а интерфейсы, реализованные классом, являются частью его экспортируемого API, такая практика сводится к помещению деталей реализации в API, что считалось неуместным, например, Java-дизайнер Джошуа Блох . [ 1 ] В общем, сбор системных констант в классы, независимые от поведения, может привести к плохому объектно-ориентированному проектированию, поскольку часто является признаком низкой связности . По этим причинам константные интерфейсы можно считать антишаблоном .
Использование этого шаблона имеет и другие недостатки: [ оригинальное исследование? ]
- класса Он загрязняет пространство имен переменными, доступными только для чтения, которые могут оказаться бесполезными.
- В отличие от во время компиляции тактической полезности реализации постоянного интерфейса , случайные артефакты времени выполнения не имеют практической пользы (ср. интерфейсы-маркеры , которые также не имеют методов, но полезны во время выполнения).
- Если в будущих выпусках потребуется совместимость двоичного кода , постоянный интерфейс должен навсегда оставаться интерфейсом (он не может быть преобразован в класс), даже если он не использовался в качестве интерфейса в обычном смысле.
- Без IDE, которая определяет, откуда взялась константа, отслеживание ее обратного класса или интерфейса может занять много времени.
- Экземпляр интерфейса синтаксически не более полезен, чем само имя интерфейса (поскольку у него нет методов).
- Если разработчик не проверяет какие-либо реализованные интерфейсы при добавлении константы в класс или не делает это, но делает опечатку в имени добавленной константы, значение константы может быть изменено автоматически. Рассмотрим пример 2 ниже.
Обратите внимание, что библиотеки Java сами используют постоянный шаблон интерфейса. Например, интерфейс SwingConstants [ 2 ] был выпущен в 1998 году, [ 3 ] и тогда это был разумный выбор.
Пример 1
[ редактировать ]public interface Constants {
double PI = 3.14159;
double PLANCK_CONSTANT = 6.62606896e-34;
}
public class Calculations implements Constants {
public double getReducedPlanckConstant() {
return PLANCK_CONSTANT / (2 * PI);
}
}
Пример 2
[ редактировать ]public interface Constants {
public static final int CONSTANT = 1;
}
public class Class1 implements Constants {
public static final int CONSTANT = 2; // *
public static void main(String args[]) throws Exception {
System.out.println(CONSTANT);
}
}
Перед добавлением строки, отмеченной звездочкой, при запуске Class1 печатается 1. После добавления строки Class1 печатается 2. Обе версии компилируются без предупреждений и ошибок.
Альтернативы
[ редактировать ]Многих ловушек антипаттерна можно избежать, преобразуя константный интерфейс в класс со статическими атрибутами:
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 , можно использовать статический импорт. [ 4 ] чтобы иметь возможность использовать константы без квалификатора Constants:
import static Constants.PLANCK_CONSTANT;
import static Constants.PI;
public class Calculations {
public double getReducedPlanckConstant() {
return PLANCK_CONSTANT / (2 * PI);
}
}
Константы также можно импортировать в массовом порядке с помощью оператора import static Constants.* . Это достигает тех же целей, что и использование интерфейса, позволяя ссылаться на константы без квалификатора.
В той или иной степени перечисленные выше проблемы в настоящее время решены:
- Поскольку статические члены можно импортировать специально, пространство имен класса не нужно засорять всеми членами константного интерфейса.
- Семантика времени выполнения и компиляции более тесно согласована при использовании статического импорта вместо постоянных интерфейсов.
- Скомпилированный код имеет на одно ограничение двоичной совместимости меньше (что «вычисления класса реализуют константы»).
- Поскольку статический импорт применяется только к текущему файлу (а не ко всей иерархии классов), легче обнаружить, где объявлен каждый статический член.
- Меньше необходимости объявлять переменные постоянного типа интерфейса, и потенциально яснее, что конкретного экземпляра на самом деле не существует.
Однако обратите внимание, что эти изменения не улучшают связность класса Constants и не предотвращают случайное незаметное изменение значения константы, поэтому статический импорт не следует рассматривать как панацею.
Ссылки
[ редактировать ]- ^ Блох, Джошуа, Эффективная Java, 2-е издание, стр. 98
- ^ "Константы Swing"
- ^ Что такое свинг?
- ^ "Статический импорт"