-
정적 팩토리 메서드(Static Factory Method)
-
1. 정적 팩토리 메서드의 특징
-
2. 정적 팩토리 메서드의 예제
-
(1) 단순한 정적 팩토리 메서드
-
(2) 싱글톤 패턴 적용
-
(3) 상위 타입을 반환하여 캡슐화 적용
-
(4) 불변 객체와 캐싱 적용
-
3. 정적 팩토리 메서드 vs 생성자
-
4. 정적 팩토리 메서드 네이밍 컨벤션
-
일반적인 정적 팩토리 메서드 예제
-
5. 실무 사용 예제
-
실무에서의 사용 : 정적 팩토리 메서드 + Builder 패턴
-
DTO에서 자주 사용되는 정적 팩토리 메서드 이름과 용도
-
실무에서의 사용 예시
-
6. 결론
정적 팩토리 메서드(Static Factory Method)
정적 팩토리 메서드(Static Factory Method)는 클래스의 인스턴스를 생성하는 정적(static) 메서드입니다. 일반적인 new 키워드를 사용한 생성자 호출보다 더 유연하고 가독성이 좋은 객체 생성 방법을 제공합니다.
정적 팩토리 메서드는 객체 생성을 캡슐화하는 static 메서드입니다. 이 패턴은 생성자 대신 또는 생성자와 함께 사용되어 객체 생성 과정을 더 유연하게 의미있게 만듭니다.
-> 쉽게 말해 생성자로 인스턴스를 생성하지 않고, static Method를 사용해 인스턴스를 생성하는 방식
1. 정적 팩토리 메서드의 특징
- 이름을 가질 수 있다.
- 생성자와 달리 메서드 이름을 지정할 수 있어, 반환될 객체의 의미를 명확하게 표현할 수 있다. (이름으로 생성 목적을 명확히 표현할 수 있다.)
- 호출될 때마다 새로운 객체를 생성할 필요가 없다.
- 캐싱 등을 통해 성능을 최적화할 수 있다.
- 반환 타입의 서브타입 객체를 반환할 수 있다.
- 상위 타입을 반환하면서도 실제 구현체를 숨길 수 있다.
- 인터페이스 기반 프레임워크 구현에 유용
- 이미 생성된 인스턴스를 반환할 수도 있다.
- 불필요한 객체 생성을 방지하여 성능을 최적화할 수 있다.
- 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
- 유연한 객체 생성이 가능하다.
- 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됨
- 생성자보다 더 많은 로직을 포함할 수 있다.
- 객체 생성 시 추가적인 검증, 캐싱 등의 로직을 포함할 수 있다.
2. 정적 팩토리 메서드의 예제
(1) 단순한 정적 팩토리 메서드
public class Car {
private String model;
// private 생성자 (외부에서 직접 생성 제한)
private Car(String model) {
this.model = model;
}
// 정적 팩토리 메서드
public static Car of(String model) {
return new Car(model);
}
public String getModel() {
return model;
}
}
// 사용 예시
Car car = Car.of("Tesla Model S");
System.out.println(car.getModel()); // Tesla Model S
- of 메서드를 통해 객체를 생성하면 생성자보다 직관적인 방식으로 인스턴스를 만들 수 있다.
(2) 싱글톤 패턴 적용
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
// private 생성자 (외부에서 인스턴스 생성 차단)
private Singleton() {}
// 정적 팩토리 메서드
public static Singleton getInstance() {
return INSTANCE;
}
}
// 사용 예시
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2); // true (같은 객체 반환)
- 미리 생성된 객체를 반환하여 메모리를 절약할 수 있다.
(3) 상위 타입을 반환하여 캡슐화 적용
interface Animal {
void sound();
}
class Dog implements Animal {
@Override
public void sound() {
System.out.println("멍멍!");
}
}
class Cat implements Animal {
@Override
public void sound() {
System.out.println("야옹!");
}
}
class AnimalFactory {
// 정적 팩토리 메서드
public static Animal createAnimal(String type) {
if ("dog".equalsIgnoreCase(type)) {
return new Dog();
} else if ("cat".equalsIgnoreCase(type)) {
return new Cat();
}
throw new IllegalArgumentException("Unknown animal type");
}
}
// 사용 예시
Animal dog = AnimalFactory.createAnimal("dog");
dog.sound(); // 멍멍!
Animal cat = AnimalFactory.createAnimal("cat");
cat.sound(); // 야옹!
- Animal 인터페이스를 반환하면서 실제 구현체(Dog, Cat)를 숨길 수 있다.
- 클라이언트 코드에서 객체 생성 방식을 알 필요 없이 createAnimal 메서드만 사용하면 된다.
(4) 불변 객체와 캐싱 적용
import java.util.HashMap;
import java.util.Map;
public class User {
private static final Map<String, User> cache = new HashMap<>();
private final String name;
private User(String name) {
this.name = name;
}
// 정적 팩토리 메서드: 캐싱 적용
public static User getInstance(String name) {
return cache.computeIfAbsent(name, User::new);
}
public String getName() {
return name;
}
}
// 사용 예시
User user1 = User.getInstance("Alice");
User user2 = User.getInstance("Alice");
System.out.println(user1 == user2); // true (같은 객체 반환)
- 같은 이름의 User 객체는 하나만 생성되도록 캐싱을 적용했다.
3. 정적 팩토리 메서드 vs 생성자
비교 항목정적 팩토리 메서드생성자
이름 지정 | 가능 (of, getInstance) | 불가능 |
반환 타입 | 상위 타입(인터페이스, 추상 클래스) 가능 | 항상 해당 클래스 타입 |
객체 재사용 | 가능 (싱글톤, 캐싱) | 매번 새로운 객체 생성 |
추가 로직 포함 | 가능 (검증, 캐싱, 변환 로직 등) | 불가능 |
서브타입 반환 | 가능 | 불가능 |
4. 정적 팩토리 메서드 네이밍 컨벤션
- from: 하나의 매개변수를 받아 객체를 생성
- of: 여러 매개변수를 받아 객체를 생성
- valueOf: from과 of의 더 자세한 버전
- instance 또는 getInstance: 싱글턴 인스턴스를 반환
- create 또는 newInstance: 새로운 인스턴스를 생성
- getType: 다른 타입의 인스턴스를 생성. Type은 반환할 객체의 타입
- newType: 새로운 Type 인스턴스를 생성
- type: getType과 newType의 간결한 버전
일반적인 정적 팩토리 메서드 예제
1. of – 단순한 객체 생성을 할 때
LocalDate date = LocalDate.of(2025, 3, 1);
2. from – 특정 데이터를 변환할 때
Integer number = Integer.valueOf("42");
3. getInstance – 싱글톤 패턴이나 캐싱된 객체 반환할 때
Singleton instance = Singleton.getInstance();
4. newInstance – 매번 새로운 인스턴스를 반환할 때
Class<?> clazz = Class.forName("java.lang.String");
5. create – 복잡한 로직을 포함한 객체 생성을 할 때
Animal dog = AnimalFactory.createAnimal("dog");
5. 실무 사용 예제
실무에서의 사용 : 정적 팩토리 메서드 + Builder 패턴
DTO에서의 사용
public class UserDTO {
private String name;
private String email;
@Builder
public UserDTO(String name, String email) {
this.name = name;
this.email = email;
}
// DTO를 엔티티로 변환
public User toEntity() {
return User.builder()
.name(this.name)
.email(this.email)
.build();
}
// 엔티티로부터 DTO 생성
public static UserDTO from(User user) {
return UserDTO.builder()
.name(user.getName())
.email(user.getEmail())
.build();
}
}
엔티티에서의 사용
@Entity
@Builder
@Getter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// 기본 생성자 (JPA 요구사항)
protected User() {}
// 모든 필드를 포함한 생성자
@Builder
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// 특정 비즈니스 로직을 포함한 생성 메서드
public static User createWithDefaultEmail(String name) {
return User.builder()
.name(name)
.email(name.toLowerCase() + "@default.com")
.build();
}
}
- 위에서도 보듯이, 정적 팩토리 메서드의 주요 장점 중 하나는 메서드 이름을 통해 객체 생성 의도를 명확히 표현할 수 있다는 것.
DTO에서 자주 사용되는 정적 팩토리 메서드 이름과 용도
from : 주로 다른 객체(주로 엔티티)로부터 DTO를 생성할 때 사용한다.
public static UserDTO from(User user) {
return new UserDTO(user.getName(), user.getEmail());
}
of : 여러 매개변수(여러 개의 객체)를 받아 DTO객체를 생성할 때 사용한다.
public static UserDTO of(String name, String email) {
return new UserDTO(name, email);
}
to : DTO를 다른 타입(주로 엔티티)로 변환할 때 사용한다.
public User toEntity() {
return new User(this.name, this.email);
}
create : 새로운 DTO 인스턴스를 생성할 때 사용한다. 주로 복잡한 생성 로직이 필요할 때 사용.
public static UserDTO createWithDefaultEmail(String name) {
return new UserDTO(name, name.toLowerCase() + "@default.com");
}
toResponse : DTO를 API 응답용 객체로 변환할 때 사용한다.
public UserResponse toResponse() {
return new UserResponse(this.name, this.email);
}
fromRequest : API 요청 객체로부터 DTO를 생성할 때 사용
public static UserDTO fromRequest(CreateUserRequest request) {
return new UserDTO(request.getName(), request.getEmail());
}
실무에서의 사용 예시
public class UserDTO {
private String name;
private String email;
private UserDTO(String name, String email) {
this.name = name;
this.email = email;
}
// Entity에서 DTO 생성
public static UserDTO from(User user) {
return new UserDTO(user.getName(), user.getEmail());
}
// 요청 객체에서 DTO 생성
public static UserDTO fromRequest(CreateUserRequest request) {
return new UserDTO(request.getName(), request.getEmail());
}
// DTO를 Entity로 변환
public User toEntity() {
return new User(this.name, this.email);
}
// DTO를 응답 객체로 변환
public UserResponse toResponse() {
return new UserResponse(this.name, this.email);
}
// 여러 매개변수로 DTO 생성
public static UserDTO of(String name, String email) {
return new UserDTO(name, email);
}
}
6. 결론
정적 팩토리 메서드는 일반 생성자보다 강력하고 유연한 객체 생성 방법을 제공한다. 특히 객체 생성을 캡슐화하고, 불필요한 객체 생성을 방지하며, 코드의 가독성과 유지보수성을 향상시키는 데 큰 장점이 있다.
Java의 valueOf, of, from, getInstance 등의 메서드 패턴을 참고하여 프로젝트에서 활용하면 더 나은 설계를 할 수 있다.
연관된 글 :
참고:
gpt
'개발 > Java' 카테고리의 다른 글
[JAVA] WAR vs JAR 차이점 (0) | 2025.03.02 |
---|---|
[Java] Stream API - map과 flatMap (1) | 2024.06.06 |
POJO(Plain Old Java Object) (0) | 2024.06.06 |
[JAVA] DTO, VO, DAO, Entity 의 차이 (0) | 2023.05.25 |
[JAVA] Lombok 어노테이션 (0) | 2023.04.29 |
정적 팩토리 메서드(Static Factory Method)
정적 팩토리 메서드(Static Factory Method)는 클래스의 인스턴스를 생성하는 정적(static) 메서드입니다. 일반적인 new 키워드를 사용한 생성자 호출보다 더 유연하고 가독성이 좋은 객체 생성 방법을 제공합니다.
정적 팩토리 메서드는 객체 생성을 캡슐화하는 static 메서드입니다. 이 패턴은 생성자 대신 또는 생성자와 함께 사용되어 객체 생성 과정을 더 유연하게 의미있게 만듭니다.
-> 쉽게 말해 생성자로 인스턴스를 생성하지 않고, static Method를 사용해 인스턴스를 생성하는 방식
1. 정적 팩토리 메서드의 특징
- 이름을 가질 수 있다.
- 생성자와 달리 메서드 이름을 지정할 수 있어, 반환될 객체의 의미를 명확하게 표현할 수 있다. (이름으로 생성 목적을 명확히 표현할 수 있다.)
- 호출될 때마다 새로운 객체를 생성할 필요가 없다.
- 캐싱 등을 통해 성능을 최적화할 수 있다.
- 반환 타입의 서브타입 객체를 반환할 수 있다.
- 상위 타입을 반환하면서도 실제 구현체를 숨길 수 있다.
- 인터페이스 기반 프레임워크 구현에 유용
- 이미 생성된 인스턴스를 반환할 수도 있다.
- 불필요한 객체 생성을 방지하여 성능을 최적화할 수 있다.
- 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
- 유연한 객체 생성이 가능하다.
- 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됨
- 생성자보다 더 많은 로직을 포함할 수 있다.
- 객체 생성 시 추가적인 검증, 캐싱 등의 로직을 포함할 수 있다.
2. 정적 팩토리 메서드의 예제
(1) 단순한 정적 팩토리 메서드
public class Car {
private String model;
// private 생성자 (외부에서 직접 생성 제한)
private Car(String model) {
this.model = model;
}
// 정적 팩토리 메서드
public static Car of(String model) {
return new Car(model);
}
public String getModel() {
return model;
}
}
// 사용 예시
Car car = Car.of("Tesla Model S");
System.out.println(car.getModel()); // Tesla Model S
- of 메서드를 통해 객체를 생성하면 생성자보다 직관적인 방식으로 인스턴스를 만들 수 있다.
(2) 싱글톤 패턴 적용
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
// private 생성자 (외부에서 인스턴스 생성 차단)
private Singleton() {}
// 정적 팩토리 메서드
public static Singleton getInstance() {
return INSTANCE;
}
}
// 사용 예시
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2); // true (같은 객체 반환)
- 미리 생성된 객체를 반환하여 메모리를 절약할 수 있다.
(3) 상위 타입을 반환하여 캡슐화 적용
interface Animal {
void sound();
}
class Dog implements Animal {
@Override
public void sound() {
System.out.println("멍멍!");
}
}
class Cat implements Animal {
@Override
public void sound() {
System.out.println("야옹!");
}
}
class AnimalFactory {
// 정적 팩토리 메서드
public static Animal createAnimal(String type) {
if ("dog".equalsIgnoreCase(type)) {
return new Dog();
} else if ("cat".equalsIgnoreCase(type)) {
return new Cat();
}
throw new IllegalArgumentException("Unknown animal type");
}
}
// 사용 예시
Animal dog = AnimalFactory.createAnimal("dog");
dog.sound(); // 멍멍!
Animal cat = AnimalFactory.createAnimal("cat");
cat.sound(); // 야옹!
- Animal 인터페이스를 반환하면서 실제 구현체(Dog, Cat)를 숨길 수 있다.
- 클라이언트 코드에서 객체 생성 방식을 알 필요 없이 createAnimal 메서드만 사용하면 된다.
(4) 불변 객체와 캐싱 적용
import java.util.HashMap;
import java.util.Map;
public class User {
private static final Map<String, User> cache = new HashMap<>();
private final String name;
private User(String name) {
this.name = name;
}
// 정적 팩토리 메서드: 캐싱 적용
public static User getInstance(String name) {
return cache.computeIfAbsent(name, User::new);
}
public String getName() {
return name;
}
}
// 사용 예시
User user1 = User.getInstance("Alice");
User user2 = User.getInstance("Alice");
System.out.println(user1 == user2); // true (같은 객체 반환)
- 같은 이름의 User 객체는 하나만 생성되도록 캐싱을 적용했다.
3. 정적 팩토리 메서드 vs 생성자
비교 항목정적 팩토리 메서드생성자
이름 지정 | 가능 (of, getInstance) | 불가능 |
반환 타입 | 상위 타입(인터페이스, 추상 클래스) 가능 | 항상 해당 클래스 타입 |
객체 재사용 | 가능 (싱글톤, 캐싱) | 매번 새로운 객체 생성 |
추가 로직 포함 | 가능 (검증, 캐싱, 변환 로직 등) | 불가능 |
서브타입 반환 | 가능 | 불가능 |
4. 정적 팩토리 메서드 네이밍 컨벤션
- from: 하나의 매개변수를 받아 객체를 생성
- of: 여러 매개변수를 받아 객체를 생성
- valueOf: from과 of의 더 자세한 버전
- instance 또는 getInstance: 싱글턴 인스턴스를 반환
- create 또는 newInstance: 새로운 인스턴스를 생성
- getType: 다른 타입의 인스턴스를 생성. Type은 반환할 객체의 타입
- newType: 새로운 Type 인스턴스를 생성
- type: getType과 newType의 간결한 버전
일반적인 정적 팩토리 메서드 예제
1. of – 단순한 객체 생성을 할 때
LocalDate date = LocalDate.of(2025, 3, 1);
2. from – 특정 데이터를 변환할 때
Integer number = Integer.valueOf("42");
3. getInstance – 싱글톤 패턴이나 캐싱된 객체 반환할 때
Singleton instance = Singleton.getInstance();
4. newInstance – 매번 새로운 인스턴스를 반환할 때
Class<?> clazz = Class.forName("java.lang.String");
5. create – 복잡한 로직을 포함한 객체 생성을 할 때
Animal dog = AnimalFactory.createAnimal("dog");
5. 실무 사용 예제
실무에서의 사용 : 정적 팩토리 메서드 + Builder 패턴
DTO에서의 사용
public class UserDTO {
private String name;
private String email;
@Builder
public UserDTO(String name, String email) {
this.name = name;
this.email = email;
}
// DTO를 엔티티로 변환
public User toEntity() {
return User.builder()
.name(this.name)
.email(this.email)
.build();
}
// 엔티티로부터 DTO 생성
public static UserDTO from(User user) {
return UserDTO.builder()
.name(user.getName())
.email(user.getEmail())
.build();
}
}
엔티티에서의 사용
@Entity
@Builder
@Getter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// 기본 생성자 (JPA 요구사항)
protected User() {}
// 모든 필드를 포함한 생성자
@Builder
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// 특정 비즈니스 로직을 포함한 생성 메서드
public static User createWithDefaultEmail(String name) {
return User.builder()
.name(name)
.email(name.toLowerCase() + "@default.com")
.build();
}
}
- 위에서도 보듯이, 정적 팩토리 메서드의 주요 장점 중 하나는 메서드 이름을 통해 객체 생성 의도를 명확히 표현할 수 있다는 것.
DTO에서 자주 사용되는 정적 팩토리 메서드 이름과 용도
from : 주로 다른 객체(주로 엔티티)로부터 DTO를 생성할 때 사용한다.
public static UserDTO from(User user) {
return new UserDTO(user.getName(), user.getEmail());
}
of : 여러 매개변수(여러 개의 객체)를 받아 DTO객체를 생성할 때 사용한다.
public static UserDTO of(String name, String email) {
return new UserDTO(name, email);
}
to : DTO를 다른 타입(주로 엔티티)로 변환할 때 사용한다.
public User toEntity() {
return new User(this.name, this.email);
}
create : 새로운 DTO 인스턴스를 생성할 때 사용한다. 주로 복잡한 생성 로직이 필요할 때 사용.
public static UserDTO createWithDefaultEmail(String name) {
return new UserDTO(name, name.toLowerCase() + "@default.com");
}
toResponse : DTO를 API 응답용 객체로 변환할 때 사용한다.
public UserResponse toResponse() {
return new UserResponse(this.name, this.email);
}
fromRequest : API 요청 객체로부터 DTO를 생성할 때 사용
public static UserDTO fromRequest(CreateUserRequest request) {
return new UserDTO(request.getName(), request.getEmail());
}
실무에서의 사용 예시
public class UserDTO {
private String name;
private String email;
private UserDTO(String name, String email) {
this.name = name;
this.email = email;
}
// Entity에서 DTO 생성
public static UserDTO from(User user) {
return new UserDTO(user.getName(), user.getEmail());
}
// 요청 객체에서 DTO 생성
public static UserDTO fromRequest(CreateUserRequest request) {
return new UserDTO(request.getName(), request.getEmail());
}
// DTO를 Entity로 변환
public User toEntity() {
return new User(this.name, this.email);
}
// DTO를 응답 객체로 변환
public UserResponse toResponse() {
return new UserResponse(this.name, this.email);
}
// 여러 매개변수로 DTO 생성
public static UserDTO of(String name, String email) {
return new UserDTO(name, email);
}
}
6. 결론
정적 팩토리 메서드는 일반 생성자보다 강력하고 유연한 객체 생성 방법을 제공한다. 특히 객체 생성을 캡슐화하고, 불필요한 객체 생성을 방지하며, 코드의 가독성과 유지보수성을 향상시키는 데 큰 장점이 있다.
Java의 valueOf, of, from, getInstance 등의 메서드 패턴을 참고하여 프로젝트에서 활용하면 더 나은 설계를 할 수 있다.
연관된 글 :
참고:
gpt
'개발 > Java' 카테고리의 다른 글
[JAVA] WAR vs JAR 차이점 (0) | 2025.03.02 |
---|---|
[Java] Stream API - map과 flatMap (1) | 2024.06.06 |
POJO(Plain Old Java Object) (0) | 2024.06.06 |
[JAVA] DTO, VO, DAO, Entity 의 차이 (0) | 2023.05.25 |
[JAVA] Lombok 어노테이션 (0) | 2023.04.29 |