[도서]/[Effective Java]

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

dop 2021. 4. 7. 12:06

정적 팩토리 메서드

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

장점

  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