이 글은 「디자인 패턴들」 문서를 공부한 내용을 정리한 글입니다. 모든 출처는 해당 문서에 있습니다.
new
연산자 사용)하는 대신 팩토리 메서드에 대한 호출로 대체한다.새로운 제품 객체들을 반환하는 팩토리 메서드를 선언한다.
구현 방식
팩토리 메서드 | 추상 메서드 | 기본값 제공 |
---|---|---|
하위 클래스 구현 여부 | 반드시 구현해야 함 | 구현하지 않아도 기본값이 동작함 |
객체 생성 로직 | 각 하위 클래스에서 자신만의 객체 생성 로직을 정의 | • 기본값 로직이 제공됨 • 필요한 경우에만 오버라이드 가능 |
제품과 관련된 핵심 비즈니스 로직이 존재한다.
팩토리 메서드는 이 로직과 구체적인 구현을 분리하는 역할을 한다.
→ 즉, 팩토리 메서드가 어떤 객체를 생성할지 결정하는 로직만 처리한다.
앱에 새로운 객체를 추가하고 싶은 경우, 새로운 크리에이터 자식 클래스를 생성한 후 해당 클래스 내부의 팩토리 메서드를 오버라이딩 하면 된다.
프레임워크에서 표준 컴포넌트 대신 커스텀 자식 클래스를 사용하고 싶은 경우, 프레임워크 전체에서 컴포넌트들을 생성하는 코드를 단일 팩토리 메서드로 줄이고, 누구나 해당 팩토리 메서드를 오버라이드 할 수 있도록 한다.
1️⃣ 모든 제품의 공통 인터페이스를 선언한다. 인터페이스 내에는 의미 있는 메서드들을 선언해야 한다.
interface Product {
...
}
2️⃣ 크리에이터 클래스 내부에 빈 팩토리 메서드를 추가한다. 메서드의 반환 유형은 공통 제품 인터페이스와 일치해야 한다.
abstract class Factory {
public abstract Product createProduct();
}
3️⃣ 객체 생성 로직을 팩토리 메서드로 이동하여 중앙에서 관리하도록 한다.
new
키워드를 사용해 객체를 생성하는 부분을 찾는다.Product productA = new ProductA();
Product productB = new ProductB();
new
키워드 대신 팩토리 메서드를 사용하여 객체를 생성하도록 수정한다.// 수정 전
Product productA = new ProductA();
// 수정 후
Product productA = createProduct("A");
public Product createProduct(String type) {
switch(type) {
case "A":
return new ProductA();
case "B":
return new ProductB();
default:
throw new IllegalArgumentException("Unknown product type: " + type);
}
}
4️⃣ 다형성을 활용해 제품 생성 로직을 자식 클래스들로 분리한다.
class ProductAFactory extends Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
class ProductBFactory extends Factory {
@Override
public Product createProduct() {
return new ProductB();
}
}
abstract class Factory {
public Product create() {
// 공통 로직
Product product = createProduct();
initializeProduct(product);
return Product;
}
// 자식 클래스에서 오버라이딩
protected abstract Product createProduct();
private void initializeProduct(Product product) {
// 공통적인 추가 초기화 로직
...
}
}
// 사용 예시
Factory factory = new ProductAFactory();
Product product = factory.create();
5️⃣ 제품 유형이 너무 많아 모든 제품에 대해 자식 클래스들을 만드는 것이 비효율적일 때, 제어 매개변수를 활용하여 팩토리 메서드에 필요한 객체를 생성하도록 설계할 수 있다.
// Transport(운송수단) 기초 클래스
abstract class Transport {
public abstract void deliver();
}
class Truck extends Transport {
@Override
public void deliver() {
System.out.println("Deliver product by truck");
}
}
class Train extends Transport() {
@Override
public void deliver() {
System.out.println("Deliver product by train");
}
}
class Plane extends Transport() {
@Override
public void deliver() {
System.out.println("Deliver product by plane");
}
}
// Mail(우편) 기초 클래스
abstract class Mail {
//팩토리 메서드
public abstract Transport createTransport(String type);
}
// TruckMail과 TrainMail 자식 클래스 만드는 대신 매개변수를 받아 처리
class GroundMail extends Mail {
@Override
public Transport createTransport(String type) {
if(type.equalsIgnoreCase("truck") {
return new Truck();
} else if(type.equalsIgnoreCase("train") {
return new Train();
} else {
throw new IllegalArgumentException("Unknown transport type: " + type);
}
}
}
class AirMail extends Mail {
@Override
public Transport createTransport(String type) {
if(type.equalsIgnoreCase("plane") {
return new Plane();
} else {
throw new IllegalArgumentException("Unknwon transport type: " + type);
}
}
}
// 클라이언트 코드
public class UseExample {
public static void main(String[] args) {
// GroundMail 사용
Mail groundMail = new GroundMail();
Transport truck = groundMail.createTransport("truck");
truck.deliver();
Transport train = groundMail.createTransport("train");
train.deliver();
// AirMail 사용
Mail airMail = new AirMail();
Transport plane = airMail.createTransport("plane");
plane.deliver();
}
}
# 결과
Deliver product by truck
Deliver product by train
Deliver product by plane
6️⃣ 추출 후, 다음 두 가지 경우로 나누어 자식 클래스에서 기초 팩토리 메서드를 구현할 수 있다.
abstract class Mail {
public abstract Transport createTransport();
}
class AirMail extends Mail {
@Override
public Transport createTransport() {
return new Plane();
}
}
class GroundMail extends Mail {
@Override
public Transport createTransport() {
return new Truck();
}
}
abstract class Mail {
public Transport createTransport() {
return new Truck();
}
}
class AirMail extends Mail {
@Override
public Transport createTransport() {
return new Plane();
}
}
// 기초 클래스에서 제공하는 기본 동작 사용
class GroundMail extend Mail {
}
패턴 | 분석 | 비고 |
---|---|---|
추상 팩토리 | ▪ 팩토리 메서드들의 집합을 기반으로 함 ▪ 프로토타입으로 생성 메서드 구현 가능 | 확장성 높은 구상 클래스 생성 |
반복자 | 팩토리 메서드와 함께 사용하여 호환되는 반복자 반환 가능 | 컬렉션과 반복자 간 유연성 제공 |
프로토타입 | ▪ 상속 기반 아님 ▪ 복제된 객체 초기화 필요 | 특징이 팩토리 메서드와 반대임 |
템플릿 메서드 | ▪ 팩토리 메서드는 템플릿 메서드의 특수화 ▪ 팩토리 메서드는 대규모 템플릿 메서드의 단계 담당 | 구조적 유사성 제공 |
📖 참고