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

물건을 대량 생산하기 위해서는 '공장'이 필요하죠. 그렇다면 팩토리 메소드의 공장은 뭘 만들어 낼까요?

팩토리 메소드 패턴이란, 부모 클래스에 알려지지 않은 구체적인 클래스를 생성하는 패턴입니다. 자식 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴이기도 합니다.

풀어서 말하면, 객체 생성을 담당하는 클래스를 따로 만들고, 그 결과 부모 클래스는 객체 생성 시 어떤 객체가 들어오는 지 알 수 없습니다.



팩토리 메소드 패턴 구현

이제 여러분은 애플의 개발자의 삶을 지낸 뒤 정년 퇴직을 하게 되었습니다. 이대로만 지내도 풍요로운 삶을 지낼 수 있겠지만, 취미로 치킨집을 열기로 했습니다.


치킨을 만드려면 일단 레시피를 받아와야 겠죠.
전략 패턴을 응용해서 메뉴명을 받아 오겠습니다.

// 치킨들의 레시피입니다.(전략 패턴) 
// 각 치킨 메뉴 클래스들이 상속받을 것입니다.
interface Recipe {
    String menuName();
}

구체적인 치킨 클래스입니다. 고객으로부터 치킨 메뉴명이 들어오면 ChickenFactory로 부터 치킨 메뉴(객체)를 받아옵니다. 결과적으로 메소드로 치킨을 만들어냅니다.
// main함수에서 직접 생성될 클래스입니다.
class Chicken {

    private Recipe recipe;
	
	// 고객으로부터 메뉴명을 받고, 치킨 팩토리로 메뉴명을 보냅니다.
    Chicken(String menu) {

    	// 치킨 팩토리로부터 각 치킨 객체들을 만들어 치킨 레시피를 가져옵니다.
        ChickenFactory ChickenMenu = new ChickenFactory();
        recipe = ChickenMenu.getChickenRecipe(menu);
    }

    public void makeChicken() {
        System.out.println(recipe.menuName() + " 완성!");
    }
}

팩토리입니다.
'공장' 이라는 이름에 걸맞게 각 치킨 객체들을 만들어내고 있죠.
치킨 클래스로 직접 레시피를 보내지 않고, 팩토리에서 객체를 만들고 그 결과값으로 객체를 반환하고 있습니다(간접적).

class ChickenFactory {

    private Recipe recipe;

	// 객체를 생성하는 메소드입니다.
    // 각 해당되는 메뉴명에 따라 객체를 생성합니다.
    public Recipe getChickenRecipe(String menu) {
	
        if (menu.equals("fried")) {
        	// 업캐스팅하여 객체를 생성해줍니다.
            recipe = new FriedChickenRecipe();

        } else if (menu.equals("spicy")) {
            recipe = new SpicyChickenRecipe();

        } else if (menu.equals("soy")) {
            recipe = new SoyChickenRecipe();

        } else return null;

        return recipe;
    }
}

각 치킨들의 레시피입니다. 레시피 인터페이스를 상속받아 객체로 만들어지고,
메뉴 이름을 반환하는 메소드를 가집니다.

class FriedChickenRecipe implements Recipe {

    FriedChickenRecipe() {
        System.out.println("후라이드 치킨 레시피");
    }

    @Override
    public String menuName() {
        return "후라이드 치킨";
    }
}
class SpicyChickenRecipe implements Recipe {

    SpicyChickenRecipe() {
        System.out.println("양념 치킨 레시피");
    }

    @Override
    public String menuName() {
        return "양념 치킨";
    }
}
class SoyChickenRecipe implements Recipe {

    SoyChickenRecipe() {
        System.out.println("간장 치킨 레시피");
    }

    @Override
    public String menuName() {
        return "간장 치킨";
    }
}

메인 함수입니다. 고객이 치킨을 주문하면 위의 과정에 따라 치킨 레시피를 받고,
그 레시피로 치킨을 만듭니다.

public class Main {
    public static void main(String[] args) {
        Chicken friedChicken = new Chicken("fried");	// "후라이드 치킨 레시피"
        friedChicken.makeChicken();						// "후라이드 치킨 완성!"

        Chicken spicyChicken = new Chicken("spicy");	// "양념 치킨 레시피"
        spicyChicken.makeChicken();						// "양념 치킨 완성!"

        Chicken soyChicken = new Chicken("soy");		// "간장 치킨 레시피"
        soyChicken.makeChicken();						// "간장 치킨 완성!"
    }
}

주문을 받을 때마다 객체를 직접 생성하는 일 없이 팩토리에 전부 맡겨 버리니 의존성은 더욱 약해지고,
만약 신메뉴가 나와도 팩토리 클래스의 if문 추가, 신메뉴 클래스 추가 정도의 일만 하면 됩니다.
부모 클래스의 코드를 수정할 필요가 없고, 확장에도 자유로우니 개방-폐쇄 원칙도 준수하게 됩니다.



추상 팩토리

그러다가 기존에 쓰던 국산 재료가 값이 많이 올라서 수입산 재료로 바꾸려고 합니다.
닭은 국산 닭을 쓰고, 양념과 간장은 수입산을 쓰고 싶습니다.
하지만 기존의 코드로는 재료들을 세분화할 수 없습니다.
그래서 이들을 받아줄 새로운 팩토리가 필요한데, 그것이 바로 추상 팩토리입니다.
국산 재료 팩토리와 수입 재료 팩토리로 캡슐화하고, 다시 추상 팩토리로 추상화 해주겠습니다.

interface ChickenIngredientFactory {
	ChickenOrigin getChickenOrigin();
    Spice getSpice();
    Soy getSoy();
}

이렇게 닭, 양념, 간장의 원산지를 가져오는 추상 메소드를 만들어 준 뒤,
국산, 수입산 클래스를 만들어 오버라이딩 해주면 됩니다.





/////////////// 수정 예정

profile
Zero-Base to Solid-Base

0개의 댓글