질문 배경
자바는 처음부터 객체 지향 언어로 개발되었으며, 기본적으로 클래스의 인스턴스를 생성하여 문제를 해결하는 데 중점을 두었습니다. 그러나 자주 사용되는 메서드(예: 정렬, 데이터 변환)의 경우, 정적 메서드 세트를 가진 유틸리티 클래스가 등장하기 시작했습니다(예: java.util.Collections).
문제
정적 메서드는 도움 기능을 호출하는 것을 간편하게 하지만 상태를 저장하거나, 의존성을 주입하거나, 독립적으로 테스트하는 데는 적합하지 않습니다. 반면 인스턴스 클래스는 더 유연하지만 초기화에 더 많은 코드를 요구하며, 신중한 생명 주기를 필요로 합니다.
해결책
유틸리티 클래스 (utility classes) — 상태가 없는 정적 메서드의 집합으로 객체 생성을 요구하지 않습니다. 컬렉션 조작, 변환, 수학 연산에 적합합니다.
인스턴스 클래스 — 상태를 저장하고, 의존성을 사용하며, 확장성과 테스트 가능성을 보장합니다. 비즈니스 로직, 서비스, 컨트롤러 등에서 사용됩니다.
코드 예시:
// 유틸리티 클래스 예시 public class MathUtils { public static int add(int a, int b) { return a + b; } } // 사용법: int sum = MathUtils.add(1, 2); // 비즈니스 로직을 위한 클래스 인스턴스 예시 public class OrderService { private final OrderRepository repo; public OrderService(OrderRepository repo) { this.repo = repo; } public void placeOrder(Order o) { repo.save(o); } }
주요 특징들:
유틸리티 클래스를 상속하거나 정적 메서드를 확장할 수 있나요?
아니요, 일반적으로 유틸리티 클래스는 final로 선언되고, private 생성자를 가지고 있습니다. 정적 메서드의 상속은 가능하지만 의미가 없으며, 인스턴스 호출 수준에서 상속되지 않습니다.
코드 예시:
public final class MyUtils { private MyUtils() {} // 인스턴스 생성을 방지함 }
유틸리티 클래스가 상태를 가질 수 있나요?
아니요. 유틸리티 클래스가 상태(instance 또는 static 필드)를 가지고 있다면, 이는 유틸리티의 원칙을 위반하며, 멀티스레딩 문제와 가독성을 저하시킵니다.
테스트시에 유틸리티 클래스의 정적 메서드를 모킹할 수 있나요?
특별한 도구인 PowerMock을 사용하여 가능합니다. 그러나 이는 테스트를 더 복잡하고 때때로 불안정하게 만들 수 있습니다. 일반적으로 DI 친화적인 접근이 테스트에 더 적합합니다.
프로젝트에서 모든 서비스가 유틸리티 정적 메서드로 구현됨. 의존성 주입이 불가능하고, 단위 테스트는 격리되지 않으며, 각 테스트가 환경 상태에 의존함.
장점:
단점:
서비스는 인터페이스를 통해 의존성을 주입 받은 별도 클래스를 활용. 변환과 간단한 작업에는 상태가 없는 별도 유틸리티 클래스를 사용.
장점:
단점: