<aside>
💡 시작 전, 인터페이스
의 용도에 대해 정확히 상기 시키자.
인터페이스
는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다.
즉, 클래스가 어떤 인터페이스
를 구현한다는 것은, 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에게 얘기해주는 것이다. 인터페이스는 이 용도로만 사용해야 한다.
</aside>
상수 인터페이스 란, 상수를 뜻하는 static final
필드로만 가득 찬 인터페이스를 의미한다.
보통 이러한 상수들을 사용하려는 클래스에서 정규화된 이름을 쓰는 걸 피하고자 인터페이스를 구현한다.
하지만, 이는 안티패턴
이다. 이유는 아래와 같다.
Implements 할 경우 포함된 상수를 가져오기 때문에, 사용하지 않더라도 계속 가지고 있어야 된다.
클래스 내부에 사용하는 상수는 내부 구현에 사용된다. 상수 인터페이스를 구현 하면, 내부 구현을 클래스 API로 노출하는 행위이다. 즉 은닉화가 지켜지지 않았다.
상수 인터페이스의 상수들을 쓰지 않게 되더라도(다른 새로운 라이브러리 출시), 기존 레거시 코드가 사용할 수 있으므로, 이진 호환성을 위해 여전히 상수 인터페이스를 유지해야 된다.
상수 인터페이스를 구현한 클래스에서는 상수를 사용할 때 네임스페이스를 사용하지 않는다. 상수 인터페이스를 구현한 클래스의 하위 클래스에서도 네임스페이스를 사용하지 않으니, 상수 출처를 쉽게 알지 못한다. → 인터페이스가 정의한 상수들로 오염되 버린다.
상수 인터페이스를 구현한 클래스에 같은 상수가 가질 때, 클래스에 정의한 상수가 사용되므로 사용자가 의도한 흐름으로 동작하지 않을 수 있다.
public interface Constant {
static final int VALUE = 10;
}
public class Class1 implements Constant{
public static final int VALUE = 20;
public static void main(String[] args){
System.out.println(VALUE); // 클래스에 정의한 상수가 사용됨. -> 20
}
}
이처럼 상수 인터페이스를 Implements 했을 때, 대부분 문제가 발생한다.
정확히 말하면 상수 인터페이스를 쓰는것이 안티패턴
이 아니라, Implements 해서 사용하는 것 안티패턴
인 것이다.!!
상수 인터페이스를 모아 명확히 패키지 명을 지어준다면, Implements 하는 일이 없을 거라 예상되지만 결국 Implements 가능성을 열어 두는 것이다.
개인 의견
상수 인터페이스의 다양한 의견이 있지만, 초기 인터페이스의 용도에서 말했듯이 인터페이스는 공통되는 관심사항을 추상화하고 각 클래스가 구현하도록 설계하는 것이 목적이다. 즉, 각 클래스의 동작 방식을 규정하기 위해 사용되는 것이지 단순히 클래스에 사용될 상수를 제공하는 용도는 적합하지 않다.
만약 사용을 한다면, 아래와 같은 방법이 더 합당한 선택지 일 것이다.