정적 팩토리 메서드
스태틱 메서드로 객체 생성을 캡슐화한다.
장점
- 이름이 있기에 가독성이 높다
- 인스턴스 생성 없이 가능하다.
- 반환 타입의 하위 타입으로도 반환이 가능하다.
- 객체 생성을 캡슐화할 수 있다.
1. 가독성이 높다.
public class Coffee {
int price, makeTime, calories;
public Coffee(int price, int makeTime, int calories) {
this.price = price;
this.makeTime = makeTime;
this.calories = calories;
}
public static Coffee newAmericano() {
return new Coffee(4100, 5, 5);
}
public static Coffee newCafeLatte() {
return new Coffee(4600, 10, 50);
}
}
생성자 방식
Coffee americano = new Coffee(4100,5,5);
Coffee cafeLatte = new Coffee(4600,10,50);
정적 팩토리 방식
Coffee americano = Coffee.newAmericano();
Coffee cafeLatte = Coffee.newCafeLatte();
생성자 방식의 숫자 나열만 가지고는 어떤 특징을 가지고 있는지 한눈에 알기 어렵지만, 정적 팩토리 방식을 사용하면 메서드 명에서 드러나기 때문에 가독성이 좋다. (대신에 메서드명을 신중하게 작성할 필요가 있다.)
2. 매 번 새로운 객체 생성할 필요가 없다.
앞선 예제는 항상 new를 이용해 생성하였지만, 불변(immutable) 객체를 캐싱해서 쓰고 있다면, 굳이 값 비싼 new 연산을 할 필요가 없다.
public class Coffee{
int price, makeTime, calories;
private static final Coffee AMERICANO = new Coffee(4100, 5, 5);
private static final Coffee CAFFELATTE = new Coffee(4600, 10, 5);
public Coffee(int price, int makeTime, int calories) {
this.price = price;
this.makeTime = makeTime;
this.calories = calories;
}
public static Coffee getAmericano() {
return AMERICANO;
}
public static Coffee getCafeLatte() {
return CAFFELATTE;
}
}
위와 같이 static final로 정의해두고 반환만 해주면 매 번 new 연산 없이도 구현이 가능하다.
Coffee americano2 = Coffee.getAmericano();
Coffee cafeLatte2 = Coffee.getCafeLatte();
3. 서브클래스 타입으로 반환이 가능하다.
주문 번호의 길이에 따라 홀 주문인지 배달 주문인지 알 수 있다면, 다음과 같이 서브클래스를 이용해서 서브클래스로 리턴할 수 있다.
public class Order {
int price;
String menu;
public Order getOrder(int price , String menu, String orderNum){
if(isHallOrder(orderNum)){
return new HallOrder(price,menu);
}else if(isDeliveryOrder(orderNum)){
return new DeliveryOrder(price,menu);
}else{
return null;
}
}
private boolean isHallOrder(String orderNum){
return orderNum.length() == 12;
}
private boolean isDeliveryOrder(String orderNum){
return orderNum.length() == 16;
}
}
class HallOrder extends Order {
int tableNum;
public HallOrder(int price, String menu){
this.price =price;
this.menu=menu;
this.tableNum = 1;
}
}
class DeliveryOrder extends Order {
int deliveryNum;
public DeliveryOrder(int price, String menu){
this.price =price;
this.menu=menu;
this.deliveryNum = 1;
}
}
이렇게 반환된 타입은 instanceof 연산으로 타입 체크를 함으로써 분기를 나눌 수 있다.
Order order1 = Order.getOrder(5000,"americano","123456123456");
Order order2 = Order.getOrder(5500,"caffeLatte","1234567812345678");
System.out.println(order1.getClass()+ " "+ (order1 instanceof HallOrder));
System.out.println( order2.getClass()+ " "+ (order2 instanceof DeliveryOrder));
4. 객체 생성을 캡슐화한다.
웹 애플리케이션을 개발하다 보면 데이터 전송을 위해 DTO객체를 이용하곤 한다.
DTO는 엔티티 객체와 자유로운 형 변환이 가능해야 하는데 이때 정적 팩토리 메서드 패턴으로 내부 구현을 모르더라도 쉽게 변환이 가능하다.
public class MemberDto {
String id;
String name;
public MemberDto(String id, String name) {
this.id = id;
this.name = name;
}
public static MemberDto from(Member member) {
return new MemberDto(member.id, member.name);
}
public String toString(){
return "ID : "+this.id +" , NAME :"+ this.name;
}
}
다음과 같이 활용하여 내부 생성 구조를 노출하지 않고도 변환할 수 있다.
Member member = new Member("1",12341234,"factoryMethod");
MemberDto memberDto = MemberDto.from(member);
System.out.println(memberDto.toString());
단점
- 상속하려면 protected 나 public으로 제작해야 한다.
- 정적 팩토리 메서드만 제공하면 생성자가 없기 때문에 하위 클래스를 만들 수 없다.
- 정적 팩토리 메서드를 개발자가 구분하기 어렵다.
- 문서 이름만 가지고는 어떤 것이 정적 팩토리 메서드인지 알 수 없다.
따라서 공통 규약을 활용해서 구분하는 게 좋다.
- from : 하나의 매개변수로 객체 생성
- of : 여러 개 매개변수로 객체 생성
- instance : 인스턴스 획득
- create : 인스턴스 생성
참고 링크
https://johngrib.github.io/wiki/static-factory-method-pattern/
https://velog.io/@ljinsk3/%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C
'[도서] > [Effective Java]' 카테고리의 다른 글
[Effective Java] 빌드 패턴 (0) | 2021.04.07 |
---|