본문 바로가기

🧑🏻‍💻 Dev/Java

[Java] 정적 팩토리 메소드 (Static Factory Method)

Java 클래스에서 of(), from(), getInstance()와 같은 정적 팩토리 메소드를 많이 사용하는 것을 보고, 정적 팩토리 메소드를 사용하는 이유에 대해서 궁금하여 글을 정리하게 되었습니다.

 

 

1. 정적 팩토리 메소드(static factory method)는 무엇인가?

  • 정적 팩토리 메소드는 객체 생성의 역할을 하는 클래스 메소드이다.
  • 자바 코드를 짜봤다면 정적 팩토리 메소드를 써본 적이 있을 것입니다.
public class Example {
    public static void main(String[] args) {
        // 직접 생성자를 통해서 객체를 생성
        List<String> list1 = new ArrayList<>();
        list1.add("김씨");
        list1.add("박씨");
        list1.add("정씨");
        list1.add("이씨");
        list1.add("윤씨");
        
        // 정적 팩토리 메소드를 통해서 객체를 생성
        List<String> list2 = List.of("김씨", "박씨", "정씨", "이씨", "윤씨");
    }
}

위 코드는 List.of()를 사용하여 직접 생성자를 사용하지 않고, 리스트를 만드는 방법에 대한 코드입니다.

 

 

 

2. 정적 팩토리 메소드는 왜 사용하는가?

  • 사실 저도 코드를 짜면서 이 부분이 가장 의문이었습니다. 객체 생성은 생성자라는 것을 통해서 할 수 있는데, 굳이 왜 정적 팩토리 메소드를 사용해서 객체를 생성하는 것일까에 대한 의문입니다.
  • 정적 팩토리 메소드를 사용하는 이유를 알아보기 위해서는 생성자와 어떤 점이 다르고, 어떤 점에서 더 좋은 점이 있는지 먼저 알아야 합니다.

 

 

 

3. 정적 팩토리 메소드와 생성자의 차이

(1) 이름을 가질 수 있습니다.

  • 객체의 생성은 목적에 따라서 생성자를 구별해서 사용해야 합니다. 생성자는 이름을 가지고 있지 않기 때문에 목적에 맞게 객체를 생성하려면 해당 클래스의 내부 구조를 잘 알고 있어야 합니다
  • 정적 팩토리 메소드를 사용하면 객체 생성 목적에 맞게 이름을 지정할 수 있습니다. 이를 통해 얻을 수 있는 효과는 코드의 가독성을 더욱 높일 수 있습니다.

 

 

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

  • 생성자를 호출하여 객체를 가져올 때는 새로운 객체를 생성해야 합니다.
  • 하지만 만약 싱글톤처럼 미리 만들어져 있는 객체를 가져와서 사용해도 되는 상황이라면 굳이 생성자를 직접 호출해서 사용할 필요가 없습니다.
  • 이때 정적 팩토리 메소드를 이용하면 호출할 때마다 새로운 객체를 생성하지 않아도 됩니다. 정적 팩토리 메소드를 사용해서 객체의 생성을 제한하고 싶다면 객체의 생성자를 private로 설정하는 것이 좋습니다.

 

 

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

  • 생성자 역할을 하는 정적 팩토리 메소드는 반환값을 가지고 있기 때문에 하위 타입의 객체를 반환할 수 있습니다.
  • 아래는 점수만 입력하면 해당 점수에 맞는 티어의 타입을 반환해 주는 분기분을 팩토리 메소드에서 구현할 수 있습니다.
public class Level {
    public static Level of(int score) {
        if (score < 1000) {
            return new Bronze();
        } else if (score < 10000) {
            return new Silver();
        } else if (score < 100000) {
            return new Gold();
        } else {
            return new Platinum();
        }
    }
}

class Bronze extends Level {
    public Bronze() {
        System.out.println("브론즈 티어");
    }
}

class Silver extends Level {
    public Silver() {
        System.out.println("실버 티어");
    }
}

class Gold extends Level {
    public Gold() {
        System.out.println("골드 티어");
    }
}

class Platinum extends Level {
    public Platinum() {
        System.out.println("플래티넘 티어");
    }
}

 

 

(4) 객체 생성을 캡슐화할 수 있습니다.

  • 생성자에 private를 붙이고, 정적 팩토리 메소드 안에서 생성자를 호출함으로써 내부 상태를 외부에 드러낼 필요 없이 객체 생성 인터페이스를 단순화할 수 있습니다.
  • DTO에서 사용하는 from()을 한 가지 예시로 볼 수 있습니다.
  • StudentDto의 내부 구현을 모르고 있더라도 Student 객체를 정적 팩토리 메소드 from()의 파라미터로 넣어주면 해당 Student에 대한 Dto 객체를 얻을 수 있습니다.
public class StudentDto {
    private String name;
    private Integer age;
    
    private StudentDto(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    
    public static StudentDto from(Student student) {
        return new StudentDto(student.getName(), student.getAge());
    }
    
    // Getter, Setter 생략
}

 

 

 

객체 사이의 형변환이 필요하거나, 객체 생성이 반복되는 경우에는 생성자보다는 정적 팩토리 메소드를 활용해 보는 것이 좋을 것으로 보입니다. 

 

 

🔗 참고자료

정적 팩토리 메서드(Static Factory Method)는 왜 사용할까?