팩토리 메서드 패턴

개념

  • 객체 생성의 책임을 메서드에 위임하는 디자인 패턴
  • 일반 생성자 대신 정적 메서드를 통해 객체를 생성하고, 객체 생성에 대한 더 많은 제어 및 로직과 유연성을 제공하여 다양한 형태로 객체 생성이 가능하다.

예시 코드

class Car {
    private String name;
    
    // 생성자를 private로 선언하여 외부에서 생성자 호출 불가
    private Car(String name) { this.name = name; }
    
    // 정적 팩토리 메서드
    public static Car from(String title) {
        return new Car(name);
    }
}
  • 생성자를 private으로 선언하여 외부에서 생성자를 통한 인스턴스 생성을 막는다.
  • 메서드를 통해 객체를 생성하여 메서드의 생성 관련 로직을 추가하여 유연한 객체 생성을 할 수 있도록 한다.

특징

1. 메서드 명을 통해 생성 목적을 나타낼 수 있다.

생성 목적에 따라 생성자를 오버로딩하는 방식을 통해 다양한 형태로 구분할 순 있지만, 생성자의 특징을 구분할 순 없다. 그래서 개발자는 생성자를 통해 객체를 생성하려면 인자 순서 및 내부 구조를 알고 있어야 목적에 맞게 객체를 생성할 수 있다. 따라서, 매개변수만으로는 객체의 특성을 구분할 수 없다. 반면, 메서드는 이름을 가지고 있기 때문에 각 메서드에 생성된 객체의 특성에 맞는 이름을 부여해주어 코드의 가독성을 높일 수 있다.

2. 다양한 객체를 반환할 수 있다.

같은 타입의 객체를 반환하는 여러 정적 메서드를 제공할 수 있다. 메서드의 유연성을 통해 조건에 따라 반환되는 객체의 형태를 달리할 수 있다. 일반적으로 인자를 받아 인자의 따라 분기문을 통해 여러 타입의 인스턴스를 반환하도록 구성이 가능하다.

3. 미리 인스턴스를 생성하여 반환할 수 있다.

인스턴스를 미리 생성하고, 이를 공유하여 재사용하게 하여 불필요한 객체를 생성하는 것을 방지할 수 있다. 동일한 객체를 캐싱하여 반환하고, 이는 불변 객체의 경우에 특히 더 유용하다.

4. 서브타입의 객체를 반환할 수 있다.

public interface Animal {}

public class Dog implements Animal {}

public class Cat implements Animal {}

public class AnimalFactory {
    public static Animal createAnimal(String type) {
        if (type.equals("Dog")) {
            return new Dog();
        } else if (type.equals("Cat")) {
            return new Cat();
        } else {
            throw new IllegalArgumentException("Unknown animal type");
        }
    }
}

매서드 호출을 통해 반환할 객체의 인스턴스가 하위타입의 인스턴스가 될 수 있어 유연성을 가진다.

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

생성자를 통해 객체 생성의 경우 외부에 내부 구현을 드러내야하는 것에 반해 정적 팩토리 메서드를 통한 객체 생성의 경우 구현부를 외부로부터 숨길 수 있다. 이러한 특징으로 정보 은닉성을 가진다.

네이밍 규칙

  • 메서드의 역할을 이름을 통해 쉽게 구분하기 위해서 독자적인 네이밍 컨벤션 (Naming Convetion)이 존재한다. 이는 통상적으로 개발자들간의 규칙처럼 사용된다.

(1) from : 하나의 매개변수를 받아 해당 타입의 인스턴스를 반환하는 경우

public record ExampleResponse(
        Long id,
        String name
) {
    public static ExampleResponse from (Example example) {
        return new ExampleResponse(example.getId(), example.getName());
    }
}

(2) of : 여러개의 매개변수 받아 적절한 타입의 인스턴스를 반환하는 경우

public record Animal(
        String name
) {
    public static Animal from (String name, String type) {
        if (type.equals("Dog")) {
          return new Dog(name);
        } else if (type.equals("Cat")) {
          return new Cat(name);
        }
    }
}

(3) valueOf : from과 of 대신 통합하여 사용

(4) instance 또는 getInstance : 인스턴스를 반환하지만, 동일한 인스턴스가 반환된다는 보장은 없음

(5) create 또는 newInstance : 매번 새로운 인스턴스를 반환하는 경우

(6) getType : 팩토리 클래스와 다른 클래스의 인스턴스를 반환하는 경우

(7) newType : 팩토리 클래스와 다른 클래스의 새로운 인스턴스를 반환하는 경우