"프리코스 12일차" 정적 팩토리 메서드, 미션 코드 구현 마무리
📝 오늘의 Todo 리스트 체크
- 2주 차 미션 코드 구현 마무리 ✅
- 단위 테스트 해보기 ✅
- (학습) 정적 팩토리 메서드에 대해 공부하기 ✅
목차
- [ 2주 차 미션 코드 구현 ]
- [ 정적 팩토리 메서드 ]
- 내일 Todo 리스트
1. Utilities 폴더 추가
1주차 미션 코드 리뷰를 통해 피드백을 받은 부분들 중 하나가, 유효성을 검사하는 validation 클래스가 동시에 파싱까지 하는 것이였다.
당시에는 숫자를 문자열에서 파싱하는 것이 유효성 검증의 일환이라고 생각하여 같은 역할로 간주했다. 그러나 다시 생각해보면 Validator 메서드의 주된 역할 '유효성 검사하기'에 집중을 하면 자연스럽게 "파싱하기"가 빠지게 되고, 그럼에도 불구하고 Validator 메서드의 역할은 명확하게 그대로 유지되었다.
지금 단계에서는 이 실수가 크지 않아서 인식하지 못했다. 하지만 나중에 확장성을 고려하면 파싱과 검증을 명확히 구분하지 않는 것은 유지보수나 확장성 측면에서 문제가 될 수 있다. 즉, 메서드의 단일 책임 원칙이 위반되었다는 것을 깨달았다.
이에 이번 주 미션부터는 Utilities 폴더를 추가하여 파싱, 문자열 분리, 랜덤 수 생성 등 모든 유틸리티 클래스를 한곳에 모으는 방식을 도입했다.
2. 정규화식 정리
"자동차 경주" 미션에서 자동차 이름의 유효성을 검사하는 과정에서 알파벳 대소문자와 숫자 이외의 문자가 포함되어 있는지 확인하기 위해 정규식을 사용해야 했다. 처음에는 '알파벳 대소문자와 숫자 이외의 문자가 포함되어 있으면 예외를 처리하는 코드'가 복잡하고 길어질 것 같았지만, 정규식이 있다는 것을 떠올려 손쉽게 해결할 수 있었다.
이 경험을 계기로, 유용할 것 같은 정규식들을 찾아보고 정리했다.
3. 정적 팩토리 메서드 정의
정의는 다음과 같다. 자바에서 정적 팩토리 메서드(Static Factory Method)는 객체를 생성하기 위한 정적(static) 메서드로 new 키워드를 직접 사용하여 생성자를 호출하는 대신 사용된다. 이 메서드는 특정 클래스의 객체 생성을 보다 유연하고 제어된 방식으로 할 수 있게 해주는 중요한 설계 패턴 중 하나이다. 정적 팩토리 메서드를 사용하면 특정 클래스의 구체적인 구현체를 감추고 상위 타입이나 인터페이스를 반환할 수 있다. 이는 의존성을 줄이고 유연한 설계를 가능하게 한다.
나는 여기서 그럼 일반 생성자와 어떤 차이가 있고 어떤 상황에서 사용해야되는건지 궁금해졌다.
4. 생성자와 어떤 차이가 있는가?
정적 팩토리 메서드와 생성자의 가장 큰 차이점은 이름을 가질 수 있다는 점였다. 생성자는 클래스 이름과 동일해야 하기 때문에, 여러 방식으로 객체를 생성할 경우 구체적인 목적을 명확하게 표현하기 어렵습니다. 반면, 정적 팩토리 메서드는 의미 있는 이름을 가질 수 있어, 생성 로직을 메서드 이름을 통해 명확하게 나타낼 수 있다. 예를 들어, createAutoLotto()와 strartRacing()처럼, 생성자의 역할이 명확해지기 때문에 코드 가독성이 향상된다.
또한, 정적 팩토리 메서드는 매번 새로운 객체를 반환할 필요가 없다는 점에서도 차이가 있습니다. 생성자는 호출될 때마다 새로운 인스턴스를 반환하지만, 정적 팩토리 메서드는 캐싱을 통해 동일한 객체를 반환하거나, 특정 조건에 따라 다른 객체를 반환할 수 있다. 이를 통해 메모리 효율성을 높이거나, 불변 객체나 싱글턴 패턴처럼 객체 생성을 제어하는 경우에도 유리하다.
한 예시를 보면
1) 일반 생성자
public class Person {
private String name;
// 생성자
public Person(String name) {
this.name = name;
}
}
Person person1 = new Person("Alice");
Person person2 = new Person("Alice");
// person1과 person2는 서로 다른 객체 (참조값이 다름)
System.out.println(person1 == person2); // false
위 코드에서, new Person("Alice")는 호출할 때마다 새로운 Person 객체를 반환한다. person1과 person2는 같은 이름을 가졌지만, 서로 다른 객체다.
2) 정적 팩토리 메서드
public class Person {
private String name;
// private 생성자 (외부에서 직접 호출할 수 없음)
private Person(String name) {
this.name = name;
}
private static final Map<String, Person> personCache = new HashMap<>();
// 정적 팩토리 메서드 (캐싱된 객체 반환)
public static Person getInstance(String name) {
if (!personCache.containsKey(name)) {
personCache.put(name, new Person(name)); // 새로운 객체 생성 및 캐싱
}
return personCache.get(name); // 캐싱된 객체 반환
}
public String getName() {
return name;
}
}
// 사용 예시 (캐싱된 객체 반환)
Person person1 = Person.getInstance("Alice");
Person person2 = Person.getInstance("Alice");
// person1과 person2는 동일한 객체 (참조값이 같음)
System.out.println(person1 == person2); // true
위 코드에서는 Person.getInstance("Alice")를 호출할 때, 동일한 이름의 Person 객체가 이미 생성된 경우 새로운 객체를 생성하지 않고 캐싱된 객체를 반환한다. 따라서 person1과 person2는 같은 Person 객체를 참조하게 되는것을 확인 할 수 있다.
이처럼 정적 팩토리 메서드를 사용하면 불필요한 객체 생성을 피하고, 메모리 사용을 최적화할 수 있다.
5. 2주차 미션에 적용하기
이번 주 미션 "자동차 경주"에 정적 팩토리 메서드를 경주를 관리하는 Racing 클래스에서 다음과 같이 적용했다.
정적 팩토리 메서드를 적용하여, 생성자를 직접 사용하는 것보다 createRacing() 같은 정적 팩토리 메서드를 사용함으로써 코드의 가독성을 높일 수 있었다. 이로 인해, 메서드 이름을 통해 객체 생성의 의도를 명확하고 직관적으로 전달할 수 있었다. 특히, 이 메서드가 문자열을 기반으로 자동차 객체들을 생성하는 역할을 한다는 점이 더욱 명확해졌다.
정적 팩토리 메서드가 내부 로직에 특화된 방식으로 객체를 생성할 수 있는 유연성을 제공한다. 향후 객체 생성 방식을 변경하거나 확장할 때 생성자를 수정할 필요 없이 메서드 내부에서 객체 생성 로직을 조정할 수 있다. 또한, 이러한 방식은 객체 생성과 관련된 모든 로직(자동차 이름 분리, 유효성 검사 등)을 메서드 내부에 캡슐화하여, 생성 과정이 외부에 노출되지 않도록 한다. 외부에서는 단순히 메서드를 호출함으로써 안전하게 객체를 생성할 수 있으며 내부의 복잡한 로직에 신경 쓸 필요가 없게 되었다.
6. 내일 Todo 리스트
- 코드 리팩토링 🔳
- 기능 목록과 MVC 구조 설계 업데이트 🔳
- 클래스 다이어그램 설계 🔳
참고 자료
정적 팩토리 메서드(Static Factory Method)는 왜 사용할까?
…
tecoble.techcourse.co.kr
코드 속 new를 even 하게 익히기
미션의 기능 요구사항을 구현하는 과정에서 반복적으로 등장하는 new 키워드가 눈에 띄었습니다. 처음에는 new 키워드를 관리하기 위해 별도의 클래스를 만들어 봤지만, 이는 단순히 코드의 위치
velog.io
끝.