정적 팩토리 메서드(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의 축약형. 짧고 명확한 경우 사용. |
'디자인 패턴' 카테고리의 다른 글
[Design Pattern] 빌더 패턴 (0) | 2025.06.01 |
---|---|
[Design Pattern] 싱글톤 패턴 (0) | 2025.05.30 |
[Design Pattern] 데코레이터 패턴 (0) | 2024.12.26 |
[Design Pattern] 프록시 패턴 (0) | 2024.12.26 |
[Design Pattern] 템플릿 콜백 패턴 (0) | 2024.12.25 |