1. 필요성
에러가 발생했을 때, 에러 객체를 만들면서 동시에 메시지를 쏘는 기능을 하도록 만들어야했다. 에러 객체를 new 키워드를 써서 생성자를 통해 만들어주고, 이후에 메시지를 쏴야하는데 이미 에러는 throw 되어 그 다음 코드는 실행하지 않게 되었다.
if(error) {
throw new MessageException("에러 발생");
sendMessage(); //실행되지 않음
}
2. 해결방법
MessageException 객체의 생성자 자체에 sendMessage 로직을 넣어주면 된다! 그러나 이런 경우에는 의미를 명확하게 하기 위해서 MessageException 객체가 정적 팩토리 메서드로 만들어지도록 하는 것이 추천된다.
public static MessageException getInstanceByMessage (String errorCode, String message){
MessageException messageException = new CommonMsgException(errorCode);
try {
sendMessage(message);
} catch (JsonProcessingException e) {
log.error("### Error occurred");
}
return messageException;
}
이제 다른 곳에서 throw new MessageException(...) 을 실행하면 에러가 throw 되면서 동시에 sendMessage 로직이 실행된다.
3. 정적 팩토리 메서드
정적(static),
팩토리(인스턴스를 만드는),
메서드(메서드) 이다.
원래 어떤 객체의 인스턴스를 만드는 역할을 생성자가 한다. 그런데 정적 팩토리 메서드는 static으로 메서드를 정의해서 생성자 대신 인스턴스를 만드는 역할을 하는 것이다. 정적 팩토리 메서드를 사용하는 이유는 다음과 같다.(참조1)
1. 이름을 가질 수 있어서 의미를 명확히 할 수 있다.
: 생성자는 객체의 이름만 넣을 수 있는 반면, 정적 팩토리 메서드는 위 예제의 getInstanceByMessage와 같이 이름을 지정하여 의미를 명확히 할 수 있다. 사용자가 의미를 알고 인스턴스를 생성할 수 있게 된다.
2. 싱글톤 인스턴스를 만들 수 있다.(참조2)
: 클래스의 내용이 변하지 않는 불변 클래스라면 인스턴스를 싱글톤 패턴으로 만들어서 인스턴스 자체를 캐싱하여 재활용할 수 있다.
class Singleton {
private static Singleton singleton = null;
private Singleton() {}
static Singleton getInstance() {
if(singleton = null) {
singleton = new Singleton();
}
return singleton;
}
}
public class Main {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2) ; //true
}
}
정적 팩토리 메서드 getInstance()를 통해서 만든 인스턴스 s1, s2는 같다. 즉 Singleton 객체의 인스턴스가 캐싱된 것이다.
3. 자식 객체를 반환할 수 있다.
: 객체 생성을 조건에 따라 분기할 수 있으며, 자식 객체를 반환할 수 있게 된다.
class Order {
public static Discount createDiscountProduct(String code) throws Exception {
if(!isValidCode(code)) {
throw new Exception("잘못된 할인 코드");
}
if (isUsableCoupon(code)) {
return new Coupon(1000);
} else if (isUsablePoint(code)) {
return new Point(500);
}
throw new Exception("이미 사용한 코드");
}
}
//상속 구조
class Coupon extends Discount {}
class Point extends Discount {}
Discount를 Coupon과 Point가 상속하는 구조라면 Discount를 생성할 때 자식 객체인 Coupon과 Point을 조건에 따라 분기하여 반환할 수 있게 된다.
자바의 Collection 프레임워크도 이러한 방식으로 작성되어있다.
public interface List<E> extends Collection<E> {
static <E> List<E> of() {
return (List<E>) ImmutableCollections.ListN.EMPTY_LIST;
}
}
4. 객체 생성을 캡슐화 할 수 있다.
: 내부 구현을 밖으로 드러낼 필요가 없다. 실무에서 자주 사용하는 Entity -> DTO 간 형변환이 대표적인 예이다.
@Builder
public class ProductDto {
private String name;
private Stirng date;
public static ProductDto from(Product product) {
return enw ProductDto(product.getName(), product.getDate());
}
}
public class Main {
public static void main(String[] args) {
Product product = repository.findById(id);
//생성자
ProductDto productDto = new ProductDto(product.getName(), product.getDate());
//정적 팩토리 메서드 : 인자들을 일일이 써주면서 겉으로 드러낼 필요가 없다.
ProductDto productDto = ProductDto.from(product);
롬복의 @RequiredArgsConstructor로 of 정적 팩토리 메서드를 쉽게 만들 수 있다.
@RequiredArgsConstructor(staticName = "of")
public class Product {
private final Long id;
private final String name;
}
네이밍 규칙
- from : 하나의 매개 변수를 받아서 객체를 생성
- of : 여러개의 매개 변수를 받아서 객체를 생성
- getInstance | instance : 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
- newInstance | create : 새로운 인스턴스를 생성
- get[OtherType] : 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
- new[OtherType] : 다른 타입의 새로운 인스턴스를 생성.
주의점
public, protected로 선언된 생성자 없이 정적 팩토리 메서드만 제공하면 해당 클래스를 다른 클래스가 상속받는 구조로 만들 수 없다.
참조
1. 테코블 - 2기_보스독님-정적 팩토리 메서드는 왜 사용할까?
https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/
2. programmer life guida... - 28. 정적 팩토리 메서드 ?!
'Programming-[Backend] > Java' 카테고리의 다른 글
[TIL] 클래스 내 private 메서드 임시 테스트 @PostConstruct, Column 이름 예약어 escape 처리 (0) | 2023.05.18 |
---|---|
자바 입력 : InputStream, InputStreamReader, BufferedStream, Scanner (0) | 2022.06.02 |
[TIL][링크] 자바 리스트 stream split 처리 - groupingBy (0) | 2022.05.24 |
[TIL] 생성자 내부 멤버 메서드 실행(생성자 안에서 메서드 실행) (0) | 2022.03.07 |
[링크] isAssignableFrom 과 instanceof의 차이 (0) | 2022.03.03 |