본문 바로가기
우아한테크코스 4기/레벨1

[레벨 1 돌아보기] 정적 팩터리 메서드 사용에 대한 고민

by 나는후니 2022. 4. 12.

지난 학습로그의 말미에 아래와 같은 글을 남겼습니다.

저는 객체지향 원칙을 지키기 위해 정적 메서드 사용 자체를 줄이려고 합니다. 하지만 정적 팩터리 메서드처럼 정적 메서드의 장점을 잘 이용할 수 있는 순간에는 어김없이 static 키워드를 사용하려 합니다!

이 내용을 본 크루들이라면 정적 팩터리 메서드가 어떤 장점이 존재하는지에 대한 궁금증이 생길 것 같습니다. 저도 그렇고요.

그래서 이번 학습로그에는 레벨 1에서 제가 갖게된 정적 팩터리 메서드를 사용하는 순간에 대한 제 생각을 적어보고자 합니다.

생성자처럼 쓰는데 이름이 있다고?

자동차 경주에 만약 아래와 같은 요구사항이 있었다면 어땠을까요?

처음으로 게임에 등록한 차량은 시작 지점이 1이다. 나머지 차량은 시작지점이 0이다.

시작지점이 0인 차량과 시작지점이 1인 차량으로 나뉘게 될것입니다. 생성자에 position을 입력받도록 할 수도 있겠지만 이름을 주면 그 의미가 더 명확해질 수 있습니다.

public class Car {
    private final String name;
    private int position;

    private Car(String name, int position) {
        this.name = name;
        this.position = position;
    }

    public static Car createFirstCar(String name) {
        return this(name, 1);
    }

    public static Car createDefaultCar(String name) {
        return this(name, 0);
    }
}

위 코드처럼 메서드 명을 변경해가면서 차량을 구분하여 생성할 수 있습니다. 메서드 명을 통해 코드를 설명할 수 있는 점이 제가 느낀 정적 팩터리 메서드의 장점입니다.

생성자를 목적에 맞게 쓰기 위해

레벨 1에서 제가 정한 저만의 컨벤션은 생성자를 가볍게 사용하자 였습니다. 생성자는 인스턴스 생성 이라는 목적을 갖고 있습니다. 인스턴스 생성이라는 목적을 지키면서 가장 가볍게사용하려면 어느정도의 코드가 들어가야 할까 고민했습니다.

고민의 결과는 이렇습니다.

  1. 필드에 값 할당
  2. 인스턴스 생성을 위해 올바른 값이 들어왔는지 검증하여 존재하면 안되는 인스턴스가 생성되지 않도록 막기

이외의 로직이 들어가면 정적 팩터리 메서드를 사용하여 인스턴스를 생성할 수 있도록 규칙을 정했습니다.

즉, 생성자를 목적에 맞게 사용하기 위해 정적 팩터리 메서드는 좋은 선택지가 될 수 있던 것이죠.

인스턴스를 재사용하자

정말 자주 사용하는 인스턴스가 존재 할 때, 매번 새로운 인스턴스를 생성해야 하는가에 대한 의문이 들곤 합니다.

그럴 때는 자주 사용하는 인스턴스를 미리 생성하고 정적 팩터리 메서드를 사용하여 해당 인스턴스를 가져오는 방법이 있습니다.

아래 코드를 보시죠.

public class Huni {
    private final String name;
}

어차피 Huni라는 클래스의 name은 항상 huni입니다. 그렇다면 매번 새 인스턴스를 생성해서 사용하는 것보다 존재하는 인스턴스를 가져오는게 관리하기도 편하고 성능상 이점이 있겠죠.

public class Huni {
    private final String name;

    private Huni(String name) {
        this.name = name;
    }

    private static final Huni HUNI = this("huni");

    public static Huni getInstance() {
        return HUNI;
    }
}

이렇게 되면 Huni는 프로그램 전체에서 단 하나로 관리할 수 있습니다.

이펙티브 자바에서 나오는 여러 장점들도 더 있겠지만, 저는 위와 같은 경우에 정적 팩터리 메서드를 주로 사용하게 됐습니다.

하지만 단점 또한 당연히 있는데요.

1. 부모 클래스로 사용할 때

정적 팩터리 메서드를 사용하면 생성자의 접근제한자를 private으로 닫아 생성자로 인스턴스를 생성하는 것을 막아주는 것이 일반적입니다. 하지만 그렇게 되면 부모 클래스로 사용할 수 없게 되죠.

물론 정적 팩터리 메서드를 사용하면서 이 기능을 재사용하고 싶다면 조합을 사용하는 방법도 있습니다. 하지만 다형성의 이점은 누리지 못하게 되겠죠?

2. api의 혼란을 야기한다

정적 팩터리 메서드를 사용한 클래스를 api로 만들면 생성자의 접근제한자를 private으로 닫아두기 때문에 api 상의 constructor 지점에 어떠한 내용도 나오지 않습니다.

그래서 따로 문서화하지 않으면 어떤 메서드를 사용하여 인스턴스를 생성해야 하는지 한참 살펴보게 되죠.

이런 혼란을 방지하는 몇 가지 방법이 있습니다.

  1. 어떤 메서드를 이용하여 인스턴스를 생성할지에 대한 문서화
  2. 일반적인 정적 팩터리 메서드 명명 규칙을 따를 것

위와 같은 방법으로 단점을 어느정도 해소할 수 있을 것 같습니다.

이펙티브 자바 아이템 1번을 보면 정적 팩터리 메서드에 대한 이야기가 상세히 나와있지만 레벨 1의 저는 항상 정적 팩터리 메서드 사용 시점에 대한 고민을 해왔습니다. 이 글이 저와 같은 고민을 하는 크루들에게 도움이 되는 글이되면 좋겠습니다.