본문 바로가기
디자인 패턴

[Design Pattern] 정적 팩토리 메서드 패턴

by 감자b 2025. 4. 14.

정적 팩토리 메서드(Static Factory Method) 패턴은 객체 생성을 위한 디자인 패턴으로, 생성자 대신 정적 메서드를 통해 객체를 생성하는 패턴을 의미한다.

public class Product {
    private String name;
    private int price;
    
    // private 생성자 - 외부에서 직접 생성 불가
    private Product(String name, int price) {
        this.name = name;
        this.price = price;
    }
    
    // 정적 팩토리 메서드
    public static Product createProduct(String name, int price) {
        return new Product(name, price);
    }
}

 

그렇다면 생성자 대신 정적 팩토리 메서드 패턴을 사용하는 제공하는 이유는 무엇일까?

1. 생성자는 클래스명으로 고정되지만, 정적 팩토리 메서드는 의도를 명확히 전달하는 이름을 가질 수 있다.

2. 정적 팩토리 메서드는 자주 요청되는 객체를 미리 만들어 놓고 이를 재사용하여 불필요한 객체 생성을 피할 수 있다. 

예를 들어 Boolean.valueOf(boolean) 메서드는 새 객체를 생성하지 않고 미리 생성된 TRUE/FALSE 인스턴스를 반환한다.

public final class Boolean implements java.io.Serializable, Comparable<Boolean>, Constable {
	
	public static final Boolean TRUE = new Boolean(true);

	public static final Boolean FALSE = new Boolean(false);
    
	@IntrinsicCandidate
	public static Boolean valueOf(boolean b) {
		return (b ? TRUE : FALSE);
	}

	public static Boolean valueOf(String s) {
		return parseBoolean(s) ? TRUE : FALSE;
	}
	
	...
}

이렇게하면 여러 번 요청이 들어와도 항상 같은 객체를 반환하게 만들어, 하나의 인스턴스만 생성되도록 관리한다.

 

3. 정적 팩토리 메서드는 인터페이스나 추상 클래스를 반환 타입으로 선언해두면, 다양한 구현체 중 상황에 따라 적절한 객체를 반환할 수 있어 구현을 유연하게 바꾸거나 확장할 수 있는 장점이 있다.

public interface Vehicle {
    void drive();
}

public class Car implements Vehicle {
    @Override
    public void drive() {
        System.out.println("자동차를 운전합니다.");
    }
}

public class Truck implements Vehicle {
    @Override
    public void drive() {
        System.out.println("트럭을 운전합니다.");
    }
}

public class VehicleFactory {
    // 반환 타입은 Vehicle 인터페이스지만 실제로는 하위 타입인 Car나 Truck 객체를 반환할 수 있음
    public static Vehicle createVehicle(String type) {
        if ("car".equalsIgnoreCase(type)) {
            return new Car();  // Car 객체 반환
        } else if ("truck".equalsIgnoreCase(type)) {
            return new Truck();  // Truck 객체 반환
        }
        throw new IllegalArgumentException("Unknown type");
    }
}

 

이 예시는 클라이언트가 Vehicle 인터페이스만 알고, 구체 클래스(Car, Truck)는 알 필요 없이 VehicleFactory.createVehicle() 정적 팩토리 메서드를 통해 객체를 생성하도록 구성되어 있다.
객체 생성 책임은 팩토리 내부에 있고, 새로운 구현체가 추가되더라도 팩토리 메서드만 수정하면 되므로 클라이언트 코드를 변경하지 않고도 유연하게 확장할 수 있다.

 

하지만 정적 팩토리 메서드는 몇 가지 한계를 가지고 있다.

private 생성자를 사용하므로 외부에서 하위 클래스를 만들 수 없기 때문에 상속이 제한되며, 생성자처럼 API 문서에 자동으로 표시되지 않기 때문에, 개발자가 존재를 알지 못할 수도 있다.

따라서 명확한 명명 규칙을 따르고, 용도와 사용법을 잘 문서화해야 한다.


정적 팩토리 메서드에서 사용되는 명명 방식

메서드 이름 설명
from 하나의 매개변수를 받아 그 값을 기반으로 인스턴스를 생성할 때 사용. 예: LocalDate.from(temporal)
of 여러 매개변수를 받아 적절한 인스턴스를 생성할 때 사용. 예: List.of(a, b, c)
getInstance / instance 호출할 때마다 동일한 인스턴스를 반환할 수도 있지만, 보장하지는 않음.
예: Calendar.getInstance()
newInstance / create 호출할 때마다 항상 새로운 인스턴스를 반환함을 명확히 표현. 예: Class.newInstance()
get[Type] get | instance와 같지만 다른 클래스에서 특정 타입의 객체를 생성할 때 사용.
new[Type] new | instance와 같지만 다른 클래스에서 특정 타입의 객체를 생성할 때 사용. 예: Map.newEntry(...)
type getType, newType의 축약형. 짧고 명확한 경우 사용.