///
Search
💡

item 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 Enforce noninstantiability with a private constuctor

유틸리티 클래스는 악일까?

본문의 내용을 다루기 전, 정적 필드와 정적 메서드밖에 없는 클래스(유틸리티 클래스)의 존재에 대한 논의가 제법 있다. 엘레강트 오브젝트같이 객체지향에 깊이 다룬 책에서는 유틸 클래스 자체를 악으로 정의한 바 있다. 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