팩토리 메소드 패턴(Factory Method Pattern)

wannabeking·2022년 10월 9일
0

디자인 패턴

목록 보기
5/14
post-thumbnail

치킨 가게

후라이드, 양념 치킨을 파는 치킨 가게가 존재하여 다음과 같이 구현했다.

public class ChickenStore {

	// 생략
    
    public Chicken order(ChickenType chickenType) {
    	Chicken chicken = null;
        
        if(chickenType == FRIED) {
        	chicken = new FriedChicken();
        } else if(chickenType == SEASONED) {
        	chicken = new SeasonedChicken();
        }
        
        chicken.cook();
        chicken.box();
        
        return chicken;
    }
}

위 코드에는 문제점이 존재한다.

만약 새로운 메뉴가 추가되거나 사라진다면 ChickenStore을 수정해야한다. 어떠한 제품(치킨)을 사용할지 결정해서 사용하기 때문에 제품에 종속되어 느슨한 결합이 아니다.
또한 캡슐화가 되어있지 않기 때문에 또 다른 클래스에서 치킨을 생성한다면 같은 코드를 복사해서 사용할 것이다.


public class SimpleChickenFactory {
    
    public Chicken create(ChickenType chickenType) {
    	Chicken chicken = null;
        
        if(chickenType == FRIED) {
        	chicken = new FriedChicken();
        } else if(chickenType == SEASONED) {
        	chicken = new SeasonedChicken();
        }
        
        return chicken;
    }
}

따라서 위와 같이 간단한 팩토리(Simple Factory)를 사용하여 구상 객체를 생성해주는 팩토리 클래스를 사용하여 캡슐화를 진행할 수 있다.

이제 ChickenStore는 어떠한 치킨이 있는지 몰라도 SimpleChickenFactorycreate()를 호출하여 알맞은 치킨을 얻을 수 있다.

간단한 팩토리를 '팩토리 패턴'이라고 부르기도 하나, 디자인 패턴이 아닌 프로그래밍에서 자주 쓰이는 관용구에 가깝다.


캡슐화에는 성공하였으나 이번에는 치킨가게가 글로벌화에 도전하기 위하여 한국 스타일 치킨, 미국 스타일 치킨으로 메뉴를 나누기로 했다.

따라서 한국 스타일 후라이드, 양념 치킨과 미국 스타일 후라이드, 양념 치킨을 주문할 수 있다.

위와 같이 간단한 팩토리로 KoreanChickenFactory, AmericanChickenFactory를 만들어 해결한다면 추상적인 개념으로 묶여있지 않기 때문에 유연성이 부족하다.

팩토리 메소드 패턴을 사용하여 ChickenStore에서 조금 더 객체 지향적으로 원하는 치킨을 주문할 수 있게 구현해보자.



팩토리 메소드 패턴

우리는 치킨을 주문하는 일 자체는 ChickenStore에서 진행하면서 한국, 미국 스타일을 살릴 수 있어야 한다.

따라서 ChickenStore을 추상 클래스로 구현한 뒤 주문하는 메소드는 구현하고 치킨을 생성하는 메소드를 서브 클래스(한국, 미국 스타일)에 위임할 수 있다.

이때 치킨을 생성하는 메소드를 팩토리 메소드라 한다.

public abstract class ChickenStore {

    public Chicken order(ChickenType chickenType) {
        Chicken chicken = create(chickenType);
        chicken.cook();
        chicken.box();

        return chicken;
    }

    protected abstract Chicken create(ChickenType chickenType);
}
public class KoreanChickenStore extends ChickenStore {

    @Override
    protected Chicken create(ChickenType chickenType) {
        Chicken chicken;
        switch (chickenType) {
            case FRIED:
                chicken = new KoreanStyleFriedChicken();
                break;
            case SEASONED:
                chicken = new KoreanStyleSeasonedChicken();
                break;
            default:
                throw new RuntimeException("invalid chicken type!");
        }

        return chicken;
    }
}
public class AmericanChickenStore extends ChickenStore {

    @Override
    protected Chicken create(ChickenType chickenType) {
        Chicken chicken;
        switch (chickenType) {
            case FRIED:
                chicken = new AmericanStyleFriedChicken();
                break;
            case SEASONED:
                chicken = new AmericanStyleSeasonedChicken();
                break;
            default:
                throw new RuntimeException("invalid chicken type!");
        }

        return chicken;
    }
}

create()라는 팩토리 메소드를 각 서브클래스가 자신의 입맛대로 구현했다.

따라서 우리는 한국 스타일 후라이드 치킨을 다음과 같이 주문할 수 있다.

ChickenStore koreanStore = new KoreanChickenStore();
Chicken koreanFriedChicken = koreanStore.order(FRIED);

팩토리 메소드 패턴은 생산자(Creator) 클래스와 제품(Product) 클래스로 나뉜다.

ChickenStore는 추상 생산자 클래스로 제품을 생산할 팩토리 메소드를 정의한다.
KoreanChickenStore는 팩토리 메소드를 구현했고 이를 구상 생산자(concrete creator)라 한다.

Chicken은 제품 클래스에 해당된다.
KoreanStyleFiredChicken과 같이 Chicken을 구상한 여러 클래스가 존재한다.

생산자 클래스는 살펴보았으니 이제 제품 클래스를 살펴보자.


public abstract class Chicken {

    protected String name;

    protected String sauce;

    public String getName() {
        return name;
    }

    public String getSauce() {
        return sauce;
    }

    public void cook() {
        System.out.println("조리 중...");
    }

    public void box() {
        System.out.println("포장 중...");
    }
}
public class KoreanStyleFriedChicken extends Chicken {

    public KoreanStyleFriedChicken() {
        name = "한국 스타일 후라이드 치킨";
        sauce = "소스 없음";
    }
}

팩토리가 생성할 제품을 Chicken이라는 추상 클래스로 선언했다.

그리고 여러 종류의 치킨에 맞게 추상 제품 클래스를 구상하면 된다.

다음으로 psvm과 출력을 살펴보자.


public class Main {

    public static void main(String[] args) {
        ChickenStore koreanStore = new KoreanChickenStore();
        ChickenStore americanStore = new AmericanChickenStore();

        Chicken koreanFriedChicken = koreanStore.order(FRIED);
        output(koreanFriedChicken.getName(), koreanFriedChicken.getSauce());
        Chicken koreanSeasonedChicken = koreanStore.order(SEASONED);
        output(koreanSeasonedChicken.getName(), koreanSeasonedChicken.getSauce());

        Chicken americanFriedChicken = americanStore.order(FRIED);
        output(americanFriedChicken.getName(), americanFriedChicken.getSauce());
        Chicken americanSeasonedChicken = americanStore.order(SEASONED);
        output(americanSeasonedChicken.getName(), americanSeasonedChicken.getSauce());
    }

    public static void output(String name, String sauce) {
        System.out.println("주문하신 " + name + " 나왔습니다.");
        System.out.println("특징은 " + sauce + " 입니다.");
        System.out.println();
    }
}

팩토리 메소드를 구현한 서브 클래스를 선택할 수 있다. (KoreanChickenStore, AmericanChickenStore)

구현에 맞게 제품을 생성하기 때문에 우리는 원하는 결과를 얻을 수 있다.

출력은 다음과 같다.


팩토리 메소드 패턴은 객체를 생성할 때 필요한 인터페이스(팩토리 메소드)를 선언한다. 서브 클래스에서 이를 구현하여 어떤 클래스(제품)의 인스턴스를 만들지 결정한다.
즉, 팩토리 메소드 패턴을 사용하면 클래스 인스턴스를 만드는 일을 서브클래스에게 맡기게 된다.


모든 소스코드는 여기에서 확인할 수 있다.



profile
내일은 개발왕 😎

0개의 댓글