정적 팩토리 메서드란?


정적 팩토리 메서드란, 객체 생성의 역할을 하는 클래스 메서드 다.

왜 굳이 생성자 대신 정적 팩토리 메서드를 사용해야할까?


1. 이름을 가질 수 있다.

new 키워드를 통해 객체 생성 시 객체의 내부 구조를 잘 알고 있어야 목적에 맞게 객체를 생성할 수 있다. 하지만 정적 팩토리 메서드 사용 시 메서드 이름에 객체 생성 목적을 담아낼 수 있다.

2. 호출할 때 마다 새로운 객체를 생성할 필요가 없다.

자주 사용되는 요소의 개수가 정해져 있다면 정적 팩토리 메서드와 캐싱구조를 함께 사용하여 호출 시 새로운 객체를 생성할 필요 없이 조회가 가능하다.

생성자를 private 으로 돌려 정적 팩토리 메서드로 캐싱을 강제하고 일정 범위를 벗어난 객체를 생성을 막을 수 있다.

3. 하위 자료형 객체를 반환할 수 있다.

생성자의 역할을 하는 정적 팩토리 메서드가 반환값을 가지고 있기 때문에 상위 타입의 정적 팩토리 메서드를 통하여 자손 타입의 객체를 반환할 수 있다.

4. 객체 생성을 캡슐화할 수 있다.

생성자를 클래스의 메서드 안으로 숨기면서 내부 상태를 외부에 드러낼 필요 없이 객체 생성 인터페이스를 단순화 시킬 수 있다.

정적 팩토리 메서드 네이밍 컨벤션


  • from : 하나의 매개 변수를 받아서 객체 생성
  • of : 여러개의 매개 변수를 받아서 객체 생성
  • getInstance | instance : 인스턴스 생성. 이전에 반환했던 것과 같을 수 있음
  • newInstance | create : 새로운 인스턴스 생성
  • get[OtherType] : 다른 타입의 인스턴스 생성. 이전에 반환했던 것과 같을 수 있음
  • new[OtherType] : 다른 타입의 새로운 인스턴스 생성

적용 예시


그렇다면 Cars 에 어떻게 적용해 볼 수 있을까?

현재 Cars 의 생성자는 아래와 같다.

public Cars(String[] names) {
    this.cars = new ArrayList<>();
    for (String name : names) {
        cars.add(new Car(name));
    }
}

생성하고자 하는 자동차 객체들의 이름을 배열로 받아 생성자 내에서 새로운 객체들을 생성한 뒤 내부에 저장하는 방식을 가지고 있다.

하나의 매개변수를 받고 있기 때문에 of 를 활용할 수 있다.

private Cars(List<Car> cars) {
    this.cars = cars;
}
 
public static Cars of(String[] names) {
    return new Cars(Arrays.stream(names).map(Car::new).collect(toList()));
}

위와 같은 리팩터링을 통해 기존에 필요하던 특정 가공을 정적 팩토리 메서드에게 위임함으로써 생성자는 자신이 갖게 될 필드만을 받을 수 있게 되었다.

또한 Cars 클래스 사용자에게 정적 팩토리 메서드를 강제함으로써 해당 객체의 멤버변수가 String 배열이 아닌 특정 가공이 거친다는 의미를 전달할 수 있다.

References