정적 팩토리 메서드 패턴은 개발자가 구성한 Static Method를 통해 간접적으로 생성자를 호출하는 객체를 생성하는 디자인 패턴이다. 우리는 지금까지 객체를 인스턴스화 할때 직접적으로 생성자(Constructor)를 호출하여 생성하였는데, 별도의 객체 생성의 역할을 하는 클래스 메서드를 통해 간접적으로 객체 생성을 유도하는 것이다. 이 패턴은 다양한 이점이 있어 많이 사용되는데, 그 특징과 장점은 다음과 같다.
특징
- 정적 메서드 사용: 객체 생성을 위해 정적 메서드를 사용합니다. 이 메서드는 일반적으로 new 키워드를 사용하여 객체를 생성하는 생성자와 달리 클래스 자체의 메서드로 호출됩니다.
- 명명 가능: 메서드 이름을 통해 객체 생성의 의도를 명확하게 할 수 있습니다. 예를 들어, from 또는 of 같은 이름을 사용할 수 있습니다.
- 유연한 객체 반환: 항상 새로운 객체를 반환하는 것이 아니라, 기존에 생성된 객체를 반환하거나, 서브클래스의 객체를 반환할 수 있습니다.
장점
- 이름을 통한 가독성 향상: 메서드 이름을 통해 객체 생성의 목적을 명확히 할 수 있어 코드의 가독성이 향상됩니다.
- 캐싱: 동일한 객체가 자주 요청되는 경우, 생성된 객체를 캐싱하여 반환할 수 있어 성능을 향상시킬 수 있습니다.
- 다양한 반환 타입: 반환 타입을 유연하게 정의할 수 있어 필요에 따라 다양한 서브클래스의 객체를 반환할 수 있습니다.
- 객체 생성 로직 캡슐화: 객체 생성 로직을 메서드 내부에 캡슐화하여 코드의 응집도를 높이고, 생성 로직 변경 시 클라이언트 코드에 영향을 최소화할 수 있습니다.
위의 특징과 장점들을 사용해 멀쩡한 생성자를 냅두고 번거롭게 한단계 거쳐 정적 팩토리 메서드를 통해 객체를 생성하는지에 대한 실용성에 대해 예시를 들어 나타내겠습니다.
1. 명명 가능성
생성자는 클래스 이름과 동일하게 정해져 있어서 어떤 객체를 생성하는지 명확히 드러내기 어렵습니다. 반면, 정적 팩토리 메서드는 명명 가능하기 때문에 메서드 이름을 통해 어떤 타입의 객체를 생성하는지, 어떤 특징을 가지는 객체를 생성하는지 명확하게 나타낼 수 있습니다.
class Car {
private String brand;
private String color;
// private 생성자
private Car(String brand, String color) {
this.brand = brand;
this.color = color;
}
// 정적 팩토리 메서드 (매개변수 하나는 from 네이밍)
public static Car brandBlackFrom(String brand) {
return new Car(brand, "black");
}
// 정적 팩토리 메서드 (매개변수 여러개는 of 네이밍)
public static Car brandColorOf(String brand, String color) {
return new Car(brand, color);
}
}
여기서 "of"는 매개변수 여러개를 나타내고, "from"은 매개변수 한개를 나타냅니다.
2. 캐싱을 통한 성능 향상
객체 생성 비용이 크거나, 동일한 객체가 자주 요청되는 경우, 정적 팩토리 메서드를 사용하면 생성된 객체를 캐싱하여 재사용할 수 있습니다. 이는 성능을 향상시키고 메모리 사용을 최적화할 수 있습니다.
public class Boolean {
private final boolean value;
private static final Boolean TRUE = new Boolean(true);
private static final Boolean FALSE = new Boolean(false);
private Boolean(boolean value) {
this.value = value;
}
public static Boolean valueOf(boolean value) {
return value ? TRUE : FALSE;
}
}
여기서 Boolean.valueOf 메서드는 true나 false에 대해 이미 생성된 객체를 반환합니다. 이를 통해 불필요한 객체 생성을 피할 수 있습니다.
3. 반환 타입의 유연성
정적 팩토리 메서드는 반환 타입을 유연하게 할 수 있어, 실제 클래스의 하위 타입을 반환할 수 있습니다. 이는 인터페이스 기반 설계나 상속 구조를 사용할 때 매우 유용합니다.
public interface Animal {
void sound();
}
public class Dog implements Animal {
public void sound() {
System.out.println("Bark");
}
}
public class Cat implements Animal {
public void sound() {
System.out.println("Meow");
}
}
public class AnimalFactory {
public static Animal createAnimal(String type) {
if (type.equals("Dog")) {
return new Dog();
} else if (type.equals("Cat")) {
return new Cat();
}
throw new IllegalArgumentException("Unknown animal type");
}
}
AnimalFactory.createAnimal 메서드는 입력에 따라 Dog나 Cat 객체를 반환할 수 있습니다. 이는 유연한 설계를 가능하게 합니다.
4. 인스턴스화 제한
정적 팩토리 메서드를 사용하면 생성자를 private으로 선언하여 클래스의 인스턴스화를 제한할 수 있습니다. 이는 불변 클래스(Immutable Class)를 만들거나 싱글턴 패턴(Singleton Pattern)을 구현할 때 유용합니다.
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// private 생성자
}
public static Singleton getInstance() {
return INSTANCE;
}
}
이 예시에서 Singleton 클래스는 getInstance 메서드를 통해서만 인스턴스에 접근할 수 있습니다.
정적 팩토리 메서드 네이밍 규칙
정적 팩토리 메서드 와 다른 정적 메서드와 역할을 구분짓기 위해 독자적인 네이밍 컨벤션(Convention)이 존재한다. 단, 정적 팩토리 메서드에서의 네이밍은 단순히 선호도 의미를 넘어서 거의 법칙 정도로 사용되는 것이니, 각 네이밍의 역할에 대해 알아두는 것은 개념을 아는 것만큼 중요하다.
- from : 하나의 매개 변수를 받아서 객체를 생성
- of : 여러개의 매개 변수를 받아서 객체를 생성
- getInstance | instance : 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음
- newInstance | create : 항상 새로운 인스턴스를 생성
- get[OrderType] : 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음
- new[OrderType] : 항상 다른 타입의 새로운 인스턴스를 생성
'Spring' 카테고리의 다른 글
FCM 푸시 알림, SQS + Lambda로 리팩토링 (3) | 2024.09.02 |
---|---|
Error creating bean with name 'jpaAuditingHandler' 에러 해결 (0) | 2024.02.01 |
멀티모듈 서비스 레이어 분리 (0) | 2024.02.01 |
ApplicationTests > contextLoads() FAILED 에러 해결 (1) | 2024.02.01 |
메세지 큐를 활용한 트랜잭션 관리 - 기술적 챌린지 (1) | 2023.08.22 |