Рубрики
Без рубрики

Анти-Шаблон интерфейса Java Constants

Как вы определяете и используете константы в Java? Большинство советов в Интернете имеют следующие мнения: D… Помеченный как java, лучшие практики.

Как вы определяете и используете константы в Java?

Большинство советов в Интернете содержат следующие мнения:

  1. Объявить public static final для констант в классе
  2. Не используйте интерфейсы для констант

Наиболее распространенный способ определения константы – в классе и с использованием public static final . Затем можно использовать константу в другом классе, используя className. ИМЯ_КОНСТАНТА . Константы обычно определяются в верхнем регистре, как правило, по крайней мере, в Java.

Итак, если бы я определил константу для значения Pi (π), это было бы что-то вроде:

public final class Constants {
    public static final double PI = 3.14;
}

Затем это можно использовать как Константы. PI всякий раз, когда мы хотим сослаться на значение Pi.

Другой способ определения констант – это использование интерфейсов.

public interface Constants {
    double PI = 3.14;
} 

Однако большинство источников в Интернете не рекомендуют это делать. Почему? Потому что это анти-паттерн.

Но действительно ли это Анти-паттерн?

Давайте рассмотрим разницу, используя оба метода.

  1. Создание класса констант:
package constants;

public final class MathConstantsClass {
    public static final double PI = 3.14;
}
  1. Создание интерфейса:
package constants;

public interface MathConstantsInterface {
    double PI = 3.14;
}

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

package operations;

public interface CircleArea {
    double calculate(double radius);
}

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

Следующий класс предоставляет реализацию вычисления площади окружности.

import constants.MathConstantsClass;
import operations.CircleArea;

public class MathConstantsClassImplementation implements CircleArea {
    public double calculate(double radius) {
        return MathConstantsClass.PI * radius * radius;
    }
}

Чтобы протестировать приведенный выше код, давайте напишем тестовый класс с использованием JUnit.

import operations.CircleArea;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MathConstantsClassImplementationTest {

    @Test
    void calculate() {
        CircleArea area = new MathConstantsClassImplementation();

        double circleArea = area.calculate(1.0);
        assertEquals(3.14, circleArea);
    }
}

Если вы запустите приведенный выше фрагмент тестового кода, тест пройдет успешно.

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

import constants.MathConstantsInterface;
import operations.CircleArea;

public class MathConstantsInterfaceImplementation implements MathConstantsInterface, CircleArea {
    public double calculate(double radius) {
        return PI * radius * radius;
    }
}

Аналогичным образом, тест для вышеупомянутого класса выглядит следующим образом:

import operations.CircleArea;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MathConstantsInterfaceImplementationTest {

    @Test
    void calculate() {
        CircleArea area = new MathConstantsInterfaceImplementation();

        double circleArea = area.calculate(1.0);
        assertEquals(3.14, circleArea);
    }
}

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

Это можно лучше понять на следующем примере:

import constants.MathConstantsInterface;
import operations.CircleArea;

public class MathConstantsWithInterfaceImplementationAndConstantShadowing implements MathConstantsInterface, CircleArea {
    private static final double PI = 200;

    public double calculate(double radius) {
        return PI * radius * radius;
    }
}

Если бы случайно кто-то переопределил значение PI внутри класса, это привело бы к неправильному выводу. Это можно легко проверить с помощью следующего теста.

import operations.CircleArea;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MathConstantsWithInterfaceImplementationAndConstantShadowingTest {

    @Test
    void calculate() {
        CircleArea area = new MathConstantsWithInterfaceImplementationAndConstantShadowing();

        double circleArea = area.calculate(1.0);
        assertEquals(3.14, circleArea);
    }
}

Приведенный выше тест завершается неудачей. Ответ, возвращаемый calculate() , равен 200.0 вместо ожидаемого 3.14 . Другой аргумент заключается в том, что использование интерфейса приведет к загрязнению пространства имен, а также приведет к распространению значения по подклассам.

Приведенные выше аргументы являются обоснованными и справедливыми.

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

Это также можно легко проверить:

import constants.MathConstantsInterface;
import operations.CircleArea;

public class MathConstantsInterfaceWithoutImplementation implements CircleArea {
    public double calculate(double radius) {
        return MathConstantsInterface.PI * radius * radius;
    }
}

Тестовый класс:

import operations.CircleArea;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MathConstantsInterfaceWithoutImplementationTest {

    @Test
    void calculate() {
        CircleArea area = new MathConstantsInterfaceWithoutImplementation();

        double circleArea = area.calculate(1.0);
        assertEquals(3.14, circleArea);
    }
}

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

public static final double PI = 3.14;

против

double PI = 3.14;

Что бы вы предпочли? Более чистый код, кто-нибудь?

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

Если, конечно, какой-нибудь разработчик не попытается реализовать класс, который содержит исключительно константы – что породило бы вопрос – ПОЧЕМУ?

Вы можете ознакомиться с кодом на моем Github: Вы можете ознакомиться с кодом на моем Github:

Рекомендации:

  1. Константы в Java: Шаблоны и Анти-шаблоны

Оригинал: “https://dev.to/darshitpp/the-java-constants-interface-anti-pattern-3cj6”