자바 API에서 LocalDate 객체를 살펴보면 객체를 생성할 때 static 메서드를 통해 얻도록 되어 있다. 예를 들어 now()를 통해 현재 날짜를 얻을 수 있고, 또는 of()를 통해 원하는 일자를 얻을 수 있다. 이러한 몇몇 클래스들의 구조를 살펴보면 대부분 생성자를 private으로 막고 static 메서드를 통해 생성하도록 구현되어 있다. 이러한 방식을 정적 팩토리 메서드라 부른다.
| Modifier and Type | Method |
|---|---|
| static LocalDate | from(TemporalAccessor temporal) |
| static LocalDate | now() |
| static LocalDate | now(Clock clock) |
| static LocalDate | now(ZoneId zone) |
| static LocalDate | of(int year, int month, int dayOfMonth) |
| static LocalDate | of(int year, Month month, int dayOfMonth) |
| static LocalDate | ofEpochDay(long epochDay) |
| static LocalDate | ofYearDay(int year, int dayOfYear) |
| static LocalDate | parse(CharSequence text) |
| static LocalDate | parse(CharSequence text, DateTimeFormatter formatter) |
왜 생성자 대신 이러한 정적 팩토리 메서드를 사용하는가? 정적 팩토리 메서드로 생성자를 대체할 때 얻을 수 있는 이점이 존재한다.
실은 IDE의 단축키가 너무 잘 되어 있어 그리 크게 느껴지는 불편함은 아니지만, 만약 String, String처럼 연속되는 타입의 파라미터를 삽입할 때 순서를 바꿔 넣는 등 실수할 가능성이 여전히 있다.
나의 경우 페이징 객체를 직접 만들어 사용하는 데 활용했다. 아래 withOrderOf의 파라미터가 많아 깔끔한 예시는 아니지만, 이렇게 하면 순서가 있는 페이징 객체를 생성하는지, 또는 기본값을 사용하는 페이징 객체를 생성하는지 함수명으로 알 수 있다.
// 매개변수 하나라면 from 네이밍, 여러개라면 of 네이밍
// withOrderOF : 요청으로 온 orderBy와 orderDirection을 사용
// of : 기본값으로 가지는 orderBy와 orderDirection을 사용
return PagingRequest.withOrderOf(requestPage, offset, limit, orderBy, orderDirection, keyword);
return PagingRequest.of(requestPage, offset, limit, keyword);
Integer.valueOf()는 내부적으로 캐싱으로 동작한다. valueOf() 또한 정적 팩토리 메서드의 네이밍 규약 중 하나이며 메서드로 작성하는 만큼 여러 분기를 처리할 수 있다. Integer.valueOf()와 IntegerCache
또한 singleton 패턴의 예제에 존재하는 getInstance() 또한 정적 팩토리 메서드 네이밍 규약 일부다. Singleton 패턴, DCLP
내가 개발 과정에서 체감한 장점은 위의 세 가지이고, 더 깊은 내용은 최하단의 블로그를 참조하자. 더불어 “하위 타입의 구현체를 생성할 수 있다”, “캡슐화한다”는 장점도 있었는데, 정적 팩토리 메서드 개념에서 이하의 내용부터는 바로 이해할 실력이 못 되어 gpt를 통해 쉬운 예제를 뽑아 공부했다.
public enum Vendor { KAKAO, TOSS, NAVER, DUMMY }
public **interface PaymentClient** {
void pay(int amount);
}
public class KakaoPaymentClient **implements PaymentClient** {
@Override
public void pay(int amount) {
System.out.println("[KAKAO] 결제 성공: " + amount + "원");
}
}
public class TossPaymentClient **implements PaymentClient** {
@Override
public void pay(int amount) {
System.out.println("[TOSS] 결제 성공: " + amount + "원");
}
} // 이하 NaverPaymentClient, DummyPaymentClient 동일