본문 바로가기
[도서]/[Effective Java]

[Effective Java] 정적 팩토리 메서드

by dop 2021. 4. 7.

정적 팩토리 메서드

스태틱 메서드로 객체 생성을 캡슐화한다.

장점

  1. 이름이 있기에 가독성이 높다
  2. 인스턴스 생성 없이 가능하다.
  3. 반환 타입의 하위 타입으로도 반환이 가능하다.
  4. 객체 생성을 캡슐화할 수 있다.

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());

단점

  1. 상속하려면 protected 나 public으로 제작해야 한다.
  • 정적 팩토리 메서드만 제공하면 생성자가 없기 때문에 하위 클래스를 만들 수 없다.
  1. 정적 팩토리 메서드를 개발자가 구분하기 어렵다.
  • 문서 이름만 가지고는 어떤 것이 정적 팩토리 메서드인지 알 수 없다.

따라서 공통 규약을 활용해서 구분하는 게 좋다.

  • 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

728x90

'[도서] > [Effective Java]' 카테고리의 다른 글

[Effective Java] 빌드 패턴  (0) 2021.04.07