팩토리 메서드 패턴
- 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하고 자식 클래스들이 생성될 객체들의 유형을 변경할 수 있도록 하는 패턴
연습 문제 코드부터 보겠습니다. 아래 내용과 요구사항을 보고 직접 리팩토링 해보세요.
public class LogisticsService {
public void planDelivery(String type) {
if (type.equals("truck")) {
Truck truck = new Truck();
System.out.println("배송 준비 중...");
truck.deliver();
System.out.println("배송 완료!");
} else if (type.equals("ship")) {
Ship ship = new Ship();
System.out.println("배송 준비 중...");
ship.deliver();
System.out.println("배송 완료!");
}
}
}
public class Main {
public static void main(String[] args) {
LogisticsService service = new LogisticsService();
service.planDelivery("truck");
}
}
public class Ship {
public void deliver() {
System.out.println("선박으로 해상 배송합니다. 📦🚢");
}
}
public class Truck {
public void deliver() {
System.out.println("트럭으로 육로 배송합니다. 📦🚚");
}
}
팩토리 메서드 패턴의 구조 및 요구사항

Creator 클래스의 주책임은 제품을 생성하는 것이 아니라 제품과 관련된 공통된 핵심 비즈니스 로직이 있고 그 사이에 제품을 생성하는 메서드를 넣음으로서 공통된 핵심 비즈니스 로직을 하나로 모으는 것이 팩토리 메서드 패턴의 포인트라고 할 수 있습니다
Creator 클래스 = Logistics 클래스 생성 (abstract)
ConcreteProduct 클래스 = Ship, Truck
ConcreteCreator 클래스 = Logistics를 상속받는 클래스
Product 클래스 = Ship Truck이 해당 인터페이스를 구현해야함


팩토리 메서드의 예시
public abstract class Logistics {
// 팩토리 메서드
abstract Transport createTransport();
// 공통 로직 (템플릿 메서드)
public void planDelivery() {
}
요구사항
- 템플릿 메서드(공통 로직)는 상속받은 구체 클래스들이 공통적으로 사용해야 하기에 abstract 메서드를 안에서 사용한다.
- 생성 메서드는 구체 클래스들이 각각 특정 객체를 생성하여 반환할 수 있도록 하는 abstract 메서드이다.
- 생성되어야 하는 객체는 같은 인터페이스를 구현해야한다.
- 팩토리 메서드는 항상 새 인스턴스를 생성할 필요 없이 캐시된 객체를 반환할 수도 있다. (하나의 ConcreteCreator 클래스에 캐싱을 적용해보세요)
정답
- Logistics 구체 클래스로 분기를 나누었고 템플릿 메서드 안에 배송 시작과 배송 완료를 넣음으로써 복잡한 if문을 단순화 시킨 것을 확인 할 수 있습니다.
- if문이 복잡하다면 concurrentHashMap으로 초기화 시킨 후 getOrDefault로 기본값(DefaultLogistics)을 반환할 수 있습니다
- RoadLogistics는 객체 생성시 새 객체를 생성할 수도 있고 기존 객체를 재사용할 수도 있게 만들었습니다.
public class Main {
public static void main(String[] args) {
String type = "truck";
Logistics logistics = new DefaultLogistics();
if (type.equals("truck")) {
logistics = new RoadLogistics();
} else if (type.equals("ship")) {
logistics = new SeaLogistics();
}
logistics.planDelivery();
}
}
public abstract class Logistics {
// 팩토리 메서드
abstract Transport createTransport();
// 공통 로직 (템플릿 메서드)
public void planDelivery() {
System.out.println("배송 준비 중...");
Transport transport = createTransport();
transport.deliver();
System.out.println("배송 완료!");
}
}
// 알 수 없는 타입에 대한 기본값 처리
public class DefaultLogistics extends Logistics{
@Override
Transport createTransport() {
return new DefaultTransport();
}
}
public class RoadLogistics extends Logistics {
private Truck cachedTruck;
@Override
public synchronized Transport createTransport() {
if (cachedTruck == null) {
cachedTruck = new Truck();
}
return cachedTruck;
}
}
public class SeaLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Ship();
}
}
public interface Transport {
void deliver();
}
public class Truck implements Transport {
public void deliver() {
System.out.println("트럭으로 육로 배송합니다. 📦🚚");
}
}
public class Ship implements Transport {
public void deliver() {
System.out.println("선박으로 해상 배송합니다. 📦🚢");
}
}
public class DefaultTransport implements Transport{
@Override
public void deliver() {
System.out.println("기본 제품입니다");
}
}
concurrentHashMap 예시
public class Main {
private static final Map<String, Logistics> LOGISTICS_MAP = new ConcurrentHashMap<>();
static {
LOGISTICS_MAP.put("truck", new RoadLogistics());
LOGISTICS_MAP.put("ship", new SeaLogistics());
}
public static Logistics create(String type) {
return LOGISTICS_MAP.getOrDefault(type, new DefaultLogistics());
}
public static void main(String[] args) {
String type = "truck";
Logistics logistics = LOGISTICS_MAP.get(type);
logistics.planDelivery();
}
}
참조
https://refactoring.guru/ko/design-patterns/factory-method
'개발지식 > Design Pattern' 카테고리의 다른 글
| 싱글톤 패턴을 구현하는 5가지 방법 (0) | 2026.01.06 |
|---|---|
| 어탭터 패턴 (객체 어댑터, 클래스 어댑터) - 개념과 연습문제 (0) | 2025.12.28 |
| 프로토타입 패턴 - 개념과 연습문제 (0) | 2025.12.24 |
| 빌더 패턴 - 개념과 연습문제 (0) | 2025.12.24 |
| 추상 팩토리 패턴 - 개념과 연습 문제 (0) | 2025.12.24 |