개발자 미니민의 개발스터디

[JAVA] 정적 팩토리 메서드 란? (static factory method)

by mini_min

 

정의

객체 생성의 역할을 하는 클래스 메서드를 의미한다. 지금까지 객체를 인스턴스화할 때, 직접 생성자 (Constructor) 을 호출했는데, 객체 생성을 해주는 별도의 클래스 메서드를 만들어 간접적으로 객체 생성을 유도하는 것이다.

💡 정적 (static), 팩토리 (factory), 메서드 (method) 팩토리는 GoF 디자인 패턴 중 팩토리 패턴에서 유래한 단어로, 객체를 생성하는 역할을 분리하겠다는 취지가 있다.

 

예시

정적 팩토리 메서드는 다음과 같이 메서드에서 생성자를 호출하고 리턴해준다.

class Car {
    private String name;

    //정적 팩토리 메서드
    public static Car of(String name){
            return new Car(name);
    }
}
public static void main(String[] args){
    //정적 메소드 호출을 통해 인스턴스화된 객체를 얻음
    Car mycar = Car.of("스포티지");
}

 

굳이 정적 팩토리 메서드를 사용하는 이유?

생성자를 두고 왜 정적 팩토리 메서드를 사용할까? 정적 팩토리 메서드 사용은 개발자가 좀 더 가독성 좋은 코드를 작성하고 객체 지향 프로그래밍을 할 수 있도록 도와준다.

😳 가령, 정적 팩토리 메서드를 사용해 이름을 부여할 수도 있고, 캡슐화도 해줄 수 있다!

 

생성자 대신 정적 팩토리 메서드를 고려해라.

굉장히 유명한 책 [이펙티브 자바] 의 저자인 조슈아 블로크“생성자 대신 정적 팩토리 메서드를 고려하라” 고 조언했다. 정적 팩토리 메서드는 어떤 특징이 있을까?

 

1. 이름을 부여할 수 있다.

객체의 목적에 따라 생성자를 구별해서 사용해야할 때, 정적 팩토리 메서드는 탁월한 이점이 있다. 정적 팩토리 메서드를 이용하면 메서드 이름에 객체의 생성 목적을 담아낼 수 있다.

public class staticFactoryMethod {

    public static void main(String[] args) {
        Cafe saleMenu = Cafe.createSaleMenu("아이스 아메리카노");
        Cafe nonSaleMenu = Cafe.createNonSaleMenu("바닐라 크림 콜드부르 라떼", 6500);
    }
}

class Cafe{
    private String menu;
    private int price;

    private Cafe(String menu, int price){
        this.menu = menu;
        this.price = price;
    }

    //정적 팩토리 메서드(매개 변수 1개)
    public static Cafe createSaleMenu(String menu){
        return new Cafe(menu, 1000);
    }

    //정적 팩토리 메서드(매개 변수 2개)
    public static Cafe createNonSaleMenu(String menu, int price){
        return new Cafe(menu, price);
    }
}
  • createSaleMenu 와 createNonSaleMenu는 모두 카페 메뉴 객체를 만들어 반환하는 정적 팩토리 메서드이다. 메서드 이름만 봐도 세일 메뉴 객체를 만들어주는지, 일반 메뉴 객체를 만들어주는지 알 수 있다.
  • new 를 이용해 생성자를 직접 만드는 것보다 이렇게 정적 팩토리 메서드를 가지고 객체를 생성하는 것이 훨씬 의미 파악하기 쉽다.

 

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

자주 사용하는 객체를 미리 생성해놓고 조회(캐싱)할 수 있는 구조를 만들 수 있다! 정적 팩토리 메서드와 캐싱 구조를 함께 사용하면, 매번 새로운 객체를 생성할 필요가 없어진다.

class Student {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

class classRoom {
    // 반에 있는 학생 객체를 저장하는 캐싱 저장소 역할
    private static final Map<String, Student> cache = new HashMap<>();

    //이미 저장된 학생 정보.
    static {
        cache.put("김민지", new Student("김민지"));
        cache.put("홍길동", new Student("홍길동"));
        cache.put("뉴진스", new Student("뉴진스"));
    }

    //정적 팩토리 메서드
    public static Student from(String name){

        if ( cache.containsKey(name) ){
            System.out.println("이미 존재하는 학생입니다.");
            return cache.get(name);
        }
        else {
            System.out.println("새로운 학생으로, 학생 정보를 등록합니다.");
            cache.put(name, new Student(name));
            return cache.get(name);
        }
    }

}
public static void main(String[] args) {

    //이미 등록된 학생 정보 가져오기
    Student select1 = classRoom.from("뉴진스");
    System.out.println(select1.getName());

    Student select2 = classRoom.from("아이브");
    System.out.println(select2.getName());

}
  • 위에 처럼, classRoom 이라는 클래스에서 객체를 저장하고 관리하는 캐싱 저장소를 만들고, 정적 팩토리 메서드로 캐싱 저장소를 조회하면, 새로운 객체를 매번 만들 필요 없이 기존 객체를 사용하며 관리할 수 있다.
  • 이렇게 새로운 객체 생성의 부담을 덜 수 있다는 장점이 있다.

 

3. 캡슐화가 가능하다.

정적 팩토리 메서드를 사용하면 외부에 내부 구현을 드러내지 않아서 캡슐화, 정보 은닉을 할 수 있다. 또한, 구현체를 숨겨 의존성을 제거해준다는 장점도 가지고 있다.

public class CafeDTO {
    private Stirng name;
    private int price;

    public static CafeDTO of(Cafe cafe) {
            return new CafeDTO(cafe.getName(), cafe.getPrice());
    }
}

//Cafe -> CafeDTO 
CafeDTO cafeDTO = CafeDTO.of(Cafe);
  • 위에 코드를 보면 메인 메서드는 정적 팩토리 메서드 of 만 호출해서 내부 구현 내용은 모른채 객체를 반환받을 수 있다.

 

정적 팩토리 메서드 네이밍

  • from : 하나의 매개 변수로 객체 생성
  • of : 여러개의 매개 변수로 객체 생성
  • getInstance / instance : 인스턴스를 생성
  • newInstance / create : 새로운 인스턴스 생성
  • get[OtherType] : 다른 타입의 인스턴스를 생성
  • new[OtherType] : 다른 타입의 새로운 인스턴스를 생성

 

 

 

블로그의 정보

개발자 미니민의 개발로그

mini_min

활동하기