소프트웨어 개발에서 복잡한 클래스의 초기화는 테스트 작성과 유지보수성을 어렵게 만드는 주요 요인 중 하나입니다. "정적 메소드 드러내기(Expose Static Method)" 기법은 이러한 문제를 해결하기 위한 효과적인 리팩토링 방법으로, 특정 메소드를 정적(static) 메소드로 변환하여 클래스 초기화 없이 직접 호출할 수 있도록 만듭니다. 이 글에서는 해당 기법의 개념, 예제 코드, 그리고 테스트 코드 작성 방법을 설명합니다.
1. 정적 메소드 드러내기란?
정적 메소드 드러내기는 다음과 같은 상황에서 유용합니다:
- 클래스 초기화 문제: 테스트 또는 사용 중 클래스 생성이 어렵거나 불필요하게 복잡한 경우.
- 독립적 로직: 메소드가 클래스의 멤버 변수에 의존하지 않고 독립적으로 동작하는 경우.
- 유틸리티화: 메소드가 특정 객체 상태와 무관하게 동작하여 공통 유틸리티로 사용될 수 있는 경우.
이 기법은 클래스의 특정 메소드를 정적 메소드로 변환하여, 객체를 생성하지 않고도 직접 호출할 수 있게 만듭니다. 이를 통해 의존성을 줄이고, 테스트와 재사용성을 높일 수 있습니다.
2. 적용 예제
기존 코드
RSCWorkflow 클래스의 validate 메소드는 내부적으로 클래스 상태를 참조하지 않지만, 여전히 객체 초기화를 요구합니다. 이는 테스트와 재사용성을 저하시킵니다.
class RSCWorkflow {
...
public void validate(Packet packet) throws InvalidFlowException {
if (packet.getOriginator().equals("MIA")
|| packet.getLength() > MAX_LENGTH
|| !packet.hasValidCheckSum()) {
throw new InvalidFlowException();
}
...
}
...
}
변경된 코드
validate 메소드를 정적 메소드로 변환하여 객체 생성 없이 호출할 수 있도록 변경합니다.
class RSCWorkflow {
public void validate(Packet packet) throws InvalidFlowException {
validatePacket(packet);
}
public static void validatePacket(Packet packet) throws InvalidFlowException {
if (packet.getOriginator().equals("MIA")
|| packet.getLength() > MAX_LENGTH
|| !packet.hasValidCheckSum()) {
throw new InvalidFlowException();
}
...
}
...
}
이제 RSCWorkflow 클래스의 정적 메소드 validatePacket을 통해 클래스 인스턴스 없이 검증 로직을 사용할 수 있습니다.
3. 주요 변경점과 이점
- 클래스 초기화 제거: 객체 생성 없이 정적 메소드를 직접 호출 가능.
- 테스트 용이성 증가: 테스트 작성 시 독립적인 정적 메소드를 Mock 또는 Stub 없이 바로 호출 가능.
- 재사용성 향상: 공통 로직을 정적 메소드로 분리하여 다른 클래스나 코드에서 쉽게 재사용.
4. 테스트 코드 작성
테스트 환경 준비
정적 메소드는 상태를 가지지 않으므로 간단히 입력과 출력만을 테스트하면 됩니다. 아래는 validatePacket 메소드를 검증하는 단위 테스트 코드입니다.
테스트 코드
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class RSCWorkflowTest {
@Test
void validatePacket_ShouldThrowException_WhenPacketInvalid() {
// Arrange
Packet invalidPacket = new Packet("MIA", RSCWorkflow.MAX_LENGTH + 1, false);
// Act & Assert
assertThrows(InvalidFlowException.class, () -> {
RSCWorkflow.validatePacket(invalidPacket);
});
}
@Test
void validatePacket_ShouldNotThrowException_WhenPacketValid() {
// Arrange
Packet validPacket = new Packet("LAX", RSCWorkflow.MAX_LENGTH - 1, true);
// Act & Assert
assertDoesNotThrow(() -> {
RSCWorkflow.validatePacket(validPacket);
});
}
}
5. 결론
정적 메소드 드러내기는 클래스의 상태와 무관하게 독립적으로 동작하는 메소드를 정적 메소드로 변환하여, 객체 초기화의 복잡성을 제거하고 테스트와 재사용성을 향상시키는 강력한 리팩토링 기법입니다. 위에서 제시한 예제와 테스트 코드를 참고하여 프로젝트에서 유사한 문제를 해결하고 코드를 보다 간결하고 유연하게 만들 수 있습니다.
'SW 개발 일반 > 레거시코드와 놀기' 카테고리의 다른 글
레거시 코드와 놀기 백서: Working Effectively with Legacy Code (0) | 2025.01.25 |
---|---|
레거시 코드와 놀기: 호출 추출과 재정의 (Extract and Override Call) (0) | 2025.01.24 |
느리지만 그나마 쉬운 테스트 개발하기 (0) | 2025.01.20 |
레거시 코드와 놀기: 전역 참조 캡슐화 (Encapsulate Global Reference) (0) | 2025.01.20 |
레거시 코드와 놀기: 메소드 객체 추출 (Breaking Out Method Object) (0) | 2025.01.19 |