Factory Method 패턴은 Creational pattern이다.
클래스를 선언할 때 new 구체적인클래스이름(); 처럼 내가 사용할 클래스를 구체적으로 명시하는 동시에 사용하는 경우가 있다. 사용할 클래스가 때때로 변경된다면 그때마다 new를 사용, 객체를 생성하게 된다.
이전에 배운 Strategy 패턴을 사용하여 전략을 변경하면 되는 것 아닌가요? 라고 물을 수 있지만 사용하는 방식이 다르다. 전략 변경만으로도 클래스가 정상 작동하면 좋겠지만 전략에 따라 요구사항이 달라지거나 동적으로 전략이 변경되어야 한다면 Strategy만으로는 클래스 생성을 감당할 수 없다. 전략을 지원하는 구체적인 클래스를 생성하고 기존 메서드도 변경되어야 한다.
👉 주어진 기능을 제공하는 클래스 생성 작업을 별도로 관리하자는 것이 Factory Method Pattern의 개념이다.
일반화를 통해 부모 클래스에서는 객체 생성 인터페이스를 제공하고 자식 클래스에서 생성될 객체의 유형을 변경할 수 있도록 하는 것도 가능하다.
문제점
말로만 하면 어려우니 예시를 보고 공부하자. 레스토랑에서 사과를 제공하고 있다. 사과의 종류로는 부사, 홍옥, 홍로가 있다. 나중에 사과 종류가 더 추가될 수도 있다! 일단 지금 레스토랑에서는 부사 사과를 제공한다.
public Apple servingApple() {
Apple apple = new Busa();
apple.wash();
apple.peel();
apple.slice();
return apple;
}
Apple apple = new Busa(); 를 통해 부사가 제공된다. 만약 홍옥 사과로 사과 종류를 변경하고 싶다면?
public Apple servingApple() {
Apple apple = new Hongok();
apple.wash();
apple.peel();
apple.slice();
return apple;
}
이렇게 기존 코드를 Hongok();으로 변경해야 한다.
public Apple servingApple(String kind) {
Apple apple = null;
if (kind.equals("Busa")) apple = new Busa();
if (kind.equals("Hongok")) apple = new Hongok();
apple.wash();
apple.peel();
apple.slice();
return apple;
}
if문을 통해 사과 종류를 그때그때 선택하는 방법도 있지만 이는 좋지 않다... Hongro라는 사과 종류가 추가된다면
if (kind.equals("Hongro")) apple = new Hongro();
새로운 if문을 사과가 사용되는 모든 경우에 추가해줘야 한다. 이런!! 그래서, 객체 생성을 주도로 맡는 클래스를 하나 추가해줬다. 이름하여 AppleFactory
public class AppleFactory {
public static Apple getApple(String kind) {
Apple apple = null;
if (kind.equals("Busa")) apple = new Busa();
if (kind.equals("Hongok")) apple = new Hongok();
if (kind.equals("Hongro")) apple = new Hongro();
return apple;
}
}
이제 레스토랑은 getApple을 통해 사과를 가져온다.
public class Restaurant {
public Apple servingApple(String kind) {
Apple apple = AppleFactory.getApple(kind);
apple.wash();
apple.peel();
apple.slice();
return apple;
}
}
만약 새로운 사과가 추가되거나 삭제되는 등 사과 수급에 변경이 생기면 Restaurant-servingApple의 코드는 수정하지 않고 AppleFactory만 수정하면 된다.
public class AppleFactory {
private static AppleFactory instance = null;
private AppleFactory() {};
public static AppleFactory getInstance() {
if (instance == null) instance = new AppleFactory();
return instance;
}
public Apple getApple(String kind) {
Apple apple = null;
if (kind.equals("Busa")) apple = new Busa();
if (kind.equals("Hongok")) apple = new Hongok();
if (kind.equals("Hongro")) apple = new Hongro();
return apple;
}
}
마지막으로 AppleFactory를 Sigleton으로 생성, 사과 제공이 하나의 인스턴스로 관리되게 한다면 완성이다.
보면 알겠지만 일반적인 팩토리 메서드 패턴은 OCP를 만족하지 않는 모습이다. 새로운 조건이 추가될 때마다 if문을 추가, 기존 코드의 수정이 필요하다.
OCP 원칙을 지키는 게 그렇게 중요하다면서 OCP도 안 지키는 팩토리 메서드 패턴을 왜 사용하나요? 🤔 new 연산자를 사용하지 않는 데서 오는 큰 장점이 있기 때문이다. 유지보수가 매우 쉬워진다!
GoF Factory Method Pattern
기존 팩토리 메서드 패턴에서 조금 더 발전된 GoF 팩토리 메서드 패턴에 대해서도 알아보자. 위 레스토랑의 사과 디저트를 서울 지점 뿐만 아니라 뉴욕 지점에도 제공하기로 했다!
서울 지점의 사과는 부사, 홍옥, 홍로 3가지 종류지만 뉴욕 지점은 다르다. 뉴욕 사과에는 코루, 크리스피, 핑크 레이디라는 종류가 있다. getApple에 뉴욕 사과를 추가하는 것도 가능하겠지만 서울 레스토랑 입장에서는 사용하지도 않을 사과 종류를 불러오는 낭비가 발생한다. 이전에 배운 Template Method를 이용하면 어떨까? (템플릿 메서드: https://codingjin0424.tistory.com/238)
public abstract class Restaurant {
public Apple ServingApple(String kind) {
Apple apple = getApple(kind);
apple.wash();
apple.peel();
apple.slice();
return apple;
}
public abstract Apple getApple(String kind);
}
서울, 뉴욕 지점을 총괄하는 Restaurant 클래스를 정의해주겠다. 두 레스토랑 모두 사과를 서빙하는 과정은 같기 때문에 공통적인 메서드를 생성해줬다. 다만 제공하는 사과 종류가 다르기 때문에 각자 레스토랑에서 재정의할 수 있는 abstract 메서드 getApple을 생성했다.
ServingApple()은 Template Method가 되고 getApple()은 Factory Method 가 된다.
public class SeoulRestaurant extends Restaurant{
@Override
public Apple getApple(String kind) {
Apple apple = null;
if (kind.equals("busa")) apple = new Busa();
if (kind.equals("hongok")) apple = new Hongok();
if (kind.equals("hongro")) apple = new Hongro();
return apple;
}
}
public class NYRestaurant extends Restaurant{
@Override
public Apple getApple(String kind) {
Apple apple = null;
if (kind.equals("koru")) apple = new Koru();
else if (kind.equals("crispy")) apple = new Everycrispy();
else if (kind.equals("pl")) apple = new Pinklady();
return apple;
}
}
서울, 뉴욕 지점은 서로 다른 getApple 메서드를 사용한다. 각자 생성하는 객체 유형을 변경하고 있다.
public static void main(String[] args) {
Restaurant namgajwa = new SeoulRestaurant();
namgajwa.ServingApple("busa");
Restaurant manha = new NYRestaurant();
manha.ServingApple("pl");
}
서울 레스토랑은 부사 사과를, 맨해튼 레스토랑은 핑크레이디 사과를 사용하여 각자의 사과 디저트를 서빙하는 모습이다.
👉 전체적인 구조는 Template Method를 따르되 변화가 있는 부분만 재정의하는 Factory Method를 사용하는 것이 GoF Factory Method Pattern이다.
https://refactoring.guru/ko/design-patterns/factory-method
팩토리 메서드 패턴
/ 디자인 패턴들 / 생성 패턴 팩토리 메서드 패턴 다음 이름으로도 불립니다: 가상 생성자, Factory Method 의도 팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지
refactoring.guru
'공부 > 소프트웨어공학' 카테고리의 다른 글
[디자인패턴] 컴포지트, 복합체 (Composite)패턴이란? (0) | 2024.12.08 |
---|---|
[디자인패턴] 추상 팩토리(Abstract Factory) 패턴이란? (0) | 2024.11.26 |
[디자인패턴] 템플릿 메서드(Template Method) 패턴이란? (0) | 2024.11.15 |
[디자인패턴] 데코레이터(Decorator) 패턴이란? (0) | 2024.11.14 |
[디자인패턴] 빌더(Builder) 패턴이란? (1) | 2024.11.12 |