상수 인터페이스 안티 패턴
인터페이스는 상수 정의를 하기 위해서 사용하지 말자!
상수를 인터페이스로 사용하는 것은 안티패턴이라한다. baeldung에서도 안티패턴이라 설명하고 있다.
// 안티패턴 예시
public interface PhysicalConstants {
static final double AVOGADROS_NUMBER = 6.022_140_857e23;
static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
static final double ELECTRON_MASS = 9.109_383_56e-31;
}
클라이언트 API 노출
인터페이스를 구현한 클래스는 해당 인터페이스의 모든 상수를 public API로 노출하게 된다.
public class MyCalculator implements PhysicalConstants {
// MyCalculator의 모든 인스턴스가 AVOGADROS_NUMBER, BOLTZMANN_CONSTANT를
// 외부에 노출하게 됨
public double calculate() {
double result = AVOGADROS_NUMBER * 100;
return result;
}
}
// 사용 예시
MyCalculator calc = new MyCalculator();
// 이런 사용이 가능해져 버림
double value = calc.AVOGADROS_NUMBER;
이런 사용은 옳아보이지는 않다.
호환성 문제
뭐든 그렇지만 한번 상수 인터페이스를 구현하면 나중에 제거하기 어렵다. 하위 호환성을 지켜야 하기에 변경하기 쉽지 않다.
// 초기 버전
public class Calculator implements PhysicalConstants {
// 상수 사용
public double calculateMoles() {
return mass / AVOGADROS_NUMBER;
}
}
// 나중에 상수가 더 이상 필요 없어진 경우
public class Calculator {
// 하위 호환성 때문에 제거할 수 없음!
// 외부 코드에서 Calculator.AVOGADROS_NUMBER를 사용 중일 수 있음
}
대안
상수 유틸리티 클래스 사용
인스턴스화 할 수 없는 유틸리티 클래스를 만들어 상수를 관리.
public class PhysicalConstants {
// 인스턴스화 방지
private PhysicalConstants() {
throw new AssertionError();
}
public static final double AVOGADROS_NUMBER = 6.022_140_857e23;
public static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
public static final double ELECTRON_MASS = 9.109_383_56e-31;
}
// 사용
public class Calculator {
public double calculate() {
return PhysicalConstants.AVOGADROS_NUMBER * 100;
}
}
static import로 간결하게:
import static com.example.PhysicalConstants.*;
public class Calculator {
public double calculate() {
return AVOGADROS_NUMBER * 100; // 클래스명 생략 가능
}
}
enum 타입 활용
관련된 상수들을 논리적으로 묶을 수 있다면 enum을 사용하는 방법도 있다.
public enum Weekday {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
클래스 내부에 상수 정의
특정 클래스와 강하게 연관된 상수라면 그 클래스 자체에 추가하는 경우도 있다.
public class Integer {
public static final int MIN_VALUE = -2147483648;
public static final int MAX_VALUE = 2147483647;
public static final int SIZE = 32;
// ...
}
// 사용
int max = Integer.MAX_VALUE;