1. 소개

이 기사에서는 공통 패턴과 안티 패턴에 초점을 맞춰 Java에서 상수를 사용하는 방법을 배울 것입니다.

상수를 정의하기 위한 몇 가지 기본 규칙부터 시작하겠습니다. 여기에서 일반적인 패턴을 살펴보기 전에 일반적인 안티 패턴으로 이동합니다.

2. 기본

상수는 정의된 후에 값이 변경되지 않는 변수입니다.

상수를 정의하기 위한 기본 사항을 살펴보겠습니다.

private static final int OUR_CONSTANT = 1;

우리가 살펴볼 패턴 중 일부는 공개 또는 비공개 액세스 수정자 결정을 다룰 것 입니다. 우리는 상수를 적이고 최종적으로 만들고 자바 프리미티브, 클래스 또는 열거형 여부에 관계없이 적절한 유형을 제공합니다 . 이름은 모두 대문자여야 하며 단어는 밑줄로 구분해야 합니다 . 때로는 스크리밍 스네이크 케이스라고도 합니다. 마지막으로 가치 자체를 제공합니다.

3. 안티 패턴

먼저, 하지 말아야 할 것을 배우는 것부터 시작합시다. Java 상수로 작업할 때 만날 수 있는 몇 가지 일반적인 안티 패턴을 살펴보겠습니다.

3.1. 매직넘버

매직 넘버는 코드 블록의 숫자 리터럴입니다.

if (number == 3.14159265359) {
    // ...
}

다른 개발자가 이해하기 어렵습니다. 또한 코드 전체에서 숫자를 사용하는 경우 값 변경을 처리하기가 어렵습니다. 대신 숫자를 상수로 정의해야 합니다.

3.2. 큰 전역 상수 클래스

프로젝트를 시작할 때 응용 프로그램에 대한 모든 상수를 정의할 의도로 Constant 또는 Utils 라는 클래스를 만드는 것이 자연스럽게 느껴질 수 있습니다 . 소규모 프로젝트의 경우 괜찮을 수 있지만 이것이 이상적인 솔루션이 아닌 몇 가지 이유를 고려해 보겠습니다.

먼저 상수 클래스에 100개 이상의 상수가 있다고 가정해 보겠습니다. 문서를 유지하고 때때로 상수를 논리적 그룹으로 리팩토링하기 위해 클래스가 유지 관리되지 않으면 읽을 수 없게 됩니다. 이름이 약간 다른 중복 상수로 끝날 수도 있습니다. 이 접근 방식은 가장 작은 프로젝트를 제외한 모든 작업에서 가독성 및 유지 관리 문제를 일으킬 수 있습니다.

Constant 클래스 자체 를 유지 관리하는 물류 외에도 이 하나의 전역 상수 클래스 및 애플리케이션의 다양한 다른 부분과 너무 많은 상호 의존성을 조장함으로써 다른 유지 관리 문제를 야기하고 있습니다.

보다 기술적인 측면 에서 Java 컴파일러는 상수 값을 사용하는 클래스의 참조 변수에 배치합니다 . 따라서 상수 클래스에서 상수 중 하나를 변경하고 참조하는 클래스가 아닌 해당 클래스만 다시 컴파일하면 일관성 없는 상수 값을 얻을 수 있습니다.

3.3. 상수 인터페이스 안티 패턴

상수 인터페이스 패턴은 특정 기능에 대한 모든 상수를 포함하는 인터페이스를 정의한 다음 인터페이스를 구현하기 위해 해당 기능이 필요한 클래스를 갖는 경우입니다.

계산기에 대한 상수 인터페이스를 정의해 보겠습니다.

public interface CalculatorConstants {
    double PI = 3.14159265359;
    double UPPER_LIMIT = 0x1.fffffffffffffP+1023;
    enum Operation {ADD, SUBTRACT, MULTIPLY, DIVIDE};
}

다음으로 CalculatorConstants 인터페이스를 구현합니다 .

public class GeometryCalculator implements CalculatorConstants {    
    public double operateOnTwoNumbers(double numberOne, double numberTwo, Operation operation) {
       // Code to do an operation
    }
}

상수 인터페이스 사용에 대한 첫 번째 주장은 인터페이스의 목적에 어긋난다는 것입니다. 우리는 인터페이스를 사용하여 구현 클래스가 제공할 동작에 대한 계약을 생성해야 합니다. 상수로 가득 찬 인터페이스를 만들 때 어떤 동작도 정의하지 않습니다.

둘째, 상수 인터페이스를 사용하면 필드 섀도잉으로 인한 런타임 문제가 발생할 수 있습니다. GeometryCalculator 클래스 에서 UPPER_LIMIT 상수 를 정의하면 어떻게 되는지 살펴보겠습니다 .

public static final double UPPER_LIMIT = 100000000000000000000.0;

GeometryCalculator 클래스 에서 해당 상수를 정의하면 해당 클래스의 CalculatorConstants 인터페이스 에서 값을 숨깁니다 . 그러면 예상치 못한 결과를 얻을 수 있습니다.

이 안티 패턴에 대한 또 다른 주장은 네임스페이스 오염을 유발한다는 것입니다. 우리 CalculatorConstants은 이제 서브 클래스의뿐만 아니라 인터페이스를 구현하는 우리의 클래스 중 하나의 네임 스페이스에있을 것입니다.

4. 패턴

앞서 우리는 상수를 정의하기 위한 적절한 형식을 살펴보았습니다. 애플리케이션 내에서 상수를 정의하기 위한 다른 좋은 사례를 살펴보겠습니다.

4.1. 일반 모범 사례

상수가 논리적으로 클래스와 관련된 경우 거기에서 상수를 정의할 수 있습니다. 우리가 열거 형의 멤버로 상수의 집합을 볼 경우, 우리가 사용할 수있는 열거 를 정의 할 수 있습니다.

Calculator 클래스 에서 몇 가지 상수를 정의해 보겠습니다 .

public class Calculator {
    public static final double PI = 3.14159265359;
    private static final double UPPER_LIMIT = 0x1.fffffffffffffP+1023;
    public enum Operation {
        ADD,
        SUBTRACT,
        DIVIDE,
        MULTIPLY
    }

    public double operateOnTwoNumbers(double numberOne, double numberTwo, Operation operation) {
        if (numberOne > UPPER_LIMIT) {
            throw new IllegalArgumentException("'numberOne' is too large");
        }
        if (numberTwo > UPPER_LIMIT) {
            throw new IllegalArgumentException("'numberTwo' is too large");
        }
        double answer = 0;
        
        switch(operation) {
            case ADD:
                answer = numberOne + numberTwo;
                break;
            case SUBTRACT:
                answer = numberOne - numberTwo;
                break;
            case DIVIDE:
                answer = numberOne / numberTwo;
                break;
            case MULTIPLY:
                answer = numberOne * numberTwo;
                break;
        }
        
        return answer;
    }
}

이 예에서는 Calculator 클래스  에서만 사용할 계획인 UPPER_LIMIT에 대한 상수를 정의 했으므로 private로 설정했습니다 . 우리는 다른 클래스가 PIOperation 열거형 을 사용할 수 있기를 원 하므로 이를 public 으로 설정했습니다 .

Operation 에 열거형사용할 때의 이점을 몇 가지 고려해 보겠습니다 . 첫 번째 장점은 가능한 값을 제한한다는 것입니다. 4개의 상수 문자열 중 하나가 제공된다는 가정 하에 우리의 메서드가 연산 값으로 문자열을 취한다고 상상해 보십시오. 메서드를 호출하는 개발자가 자신의 문자열 값을 보내는 시나리오를 쉽게 예측할 수 있습니다. 으로 열거 , 값은 우리가 정의하는 이들로 제한됩니다. 또한 열거형은 switch에서 사용하기에 특히 적합하다는 것을 알 수 있습니다 .

4.2. 상수 클래스

이제 몇 가지 일반적인 모범 사례를 살펴보았으므로 상수 클래스가 좋은 아이디어일 수 있는 경우를 고려해 보겠습니다. 응용 프로그램에 다양한 종류의 수학적 계산을 수행해야 하는 클래스 패키지가 포함되어 있다고 가정해 보겠습니다. 이 경우 계산 클래스에서 사용할 상수에 대해 해당 패키지에 상수 클래스를 정의하는 것이 합리적일 것입니다.

MathConstants 클래스를 만들어 보겠습니다 .

public final class MathConstants {
    public static final double PI = 3.14159265359;
    static final double GOLDEN_RATIO = 1.6180;
    static final double GRAVITATIONAL_ACCELERATION = 9.8;
    static final double EULERS_NUMBER = 2.7182818284590452353602874713527;
    
    public enum Operation {
        ADD,
        SUBTRACT,
        DIVIDE,
        MULTIPLY
    }
    
    private MathConstants() {
        
    }
}

가장 먼저 주목해야 할 것은 우리 클래스가 확장되지 않도록 final 이라는 입니다. 또한 인스턴스화할 수 없도록 private 생성자를 정의했습니다 . 마지막으로 이 기사의 앞부분에서 논의한 다른 모범 사례를 적용했음을 알 수 있습니다. 상수 PI패키지 외부에서 액세스해야 하기 때문에 공개 됩니다. 다른 상수는 package-private 로 남겨 두어 패키지 내에서 액세스할 수 있습니다. 우리는 모든 상수를 적이고 최종적으로 만들고 비명을 지르는 뱀의 경우에 이름을 지정했습니다. 작업은 특정 값 집합이므로 열거형을 사용했습니다. 그들을 정의합니다.

특정 패키지 수준 상수 클래스는 패키지에 지역화되어 있고 해당 패키지의 클래스와 관련된 상수를 포함하고 있기 때문에 대규모 전역 상수 클래스와 다르다는 것을 알 수 있습니다.

5. 결론

이 기사에서는 Java에서 상수를 사용할 때 볼 수 있는 가장 인기 있는 패턴 및 안티 패턴의 장단점을 고려했습니다. 안티 패턴을 다루기 전에 몇 가지 기본 서식 규칙부터 시작했습니다. 몇 가지 일반적인 안티 패턴에 대해 배운 후 상수에 자주 적용되는 패턴을 살펴보았습니다.

항상 그렇듯이 코드는 GitHub 에서 사용할 수 있습니다 .

Junit footer banner