유틸리티 클래스는 악일까?
본문의 내용을 다루기 전, 정적 필드와 정적 메서드밖에 없는 클래스(유틸리티 클래스)의 존재에 대한 논의가 제법 있다. 엘레강트 오브젝트같이 객체지향에 깊이 다룬 책에서는 유틸 클래스 자체를 악으로 정의한 바 있다. 2010년 스택오버플로우에서 논의를 살펴보면 다음과 같은 가이드가 있는데, 일리가 있다고 생각한다.
•
Design the static utility methods to be general and reusable. Make sure that they are stateless; i.e. no static variables. 재사용성과 범용성을 고려해 유틸클래스를 작성해라. 상태를 만들지 마라.
•
If you have lots of utility methods, partition them into classes in a way that will make it easy for developers to find them. 만약 유틸리티 메서드를 사용한다면, 다른 개발자가 찾기 쉬운 곳에 해당 클래스를 위치시켜라.
•
Don't use utility classes where static or instance methods in a domain class would be a better solution. For example, consider if methods in an abstract base class or an instantiable helper class would be a better solution. 유틸리티 클래스 보단 도메인 클래스에서 다룰 수 있는 정적 메서드 | 멤버 메서드라면 도메인에서 하는 것이 더 좋은 선택지다.
•
For Java 8 onwards, "default methods" in an interface may be a better option than utility classes. (See When to use: Java 8+ interface default method, vs. abstract method for example.) 자바 8부터는 인터페이스의 디폴트 메서드를 활용해 더 나은 클래스로 만들 수 있다.
유틸리티 클래스와 private 생성자
본문에도 객체지향적으로 좋은 선택지는 아니지만, 분명히 용도(valid uses)가 있다고 표현하고 있다. 원시 타입이나 배열을 다룰 때 쓰는 java.util.Math 클래스나 동일 패키지의 Arrays 클래스나, Item 1에서 다룬 것 처럼 팩토리 메서드를 포함한 유틸리티 클래스, 인터페이스가 활용할 정적 메서드를 모아둔 도우미 클래스(java.util.Collections처럼)가 그 예이다.
유틸리티 클래스는 인스턴스화를 의도하고 만든 클래스가 아니기에 인스턴스화를 막아야 한다. private 접근제어자를 붙이는 방식이 가장 확실하다. 컴파일러가 기본적으로 만들어주는 default 생성자를 사용할 수 없도록 막을 수 있다.
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
... // Remainder omitted
}
Java
복사
AssertionError를 throw 하는 건 필수는 아니지만, 클래스 내부에서 생성해서 사용하는 것을 막아준다. 본문에선 생성자를 정의했다는 것 자체가 객체를 생성하라는 신호일 수 있기에 주석을 다는 것이 좋다고 적혀있지만, 개인적으론 에러 메시지에 '인스턴스화 대상이 아닙니다.' 정도로 표현하는 게 어떨지.
용어정리
한글명 | 영어명 |
인스턴스화 | instantiate |
원시 타입 | primitive type |
유틸 클래스 | utility class |