물건을 대량 생산하기 위해서는 '공장'이 필요하죠. 그렇다면 팩토리 메소드의 공장은 뭘 만들어 낼까요?
팩토리 메소드 패턴이란, 부모 클래스에 알려지지 않은 구체적인 클래스를 생성하는 패턴입니다. 자식 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴이기도 합니다.
풀어서 말하면, 객체 생성을 담당하는 클래스를 따로 만들고, 그 결과 부모 클래스는 객체 생성 시 어떤 객체가 들어오는 지 알 수 없습니다.
이제 여러분은 애플의 개발자의 삶을 지낸 뒤 정년 퇴직을 하게 되었습니다. 이대로만 지내도 풍요로운 삶을 지낼 수 있겠지만, 취미로 치킨집을 열기로 했습니다.
치킨을 만드려면 일단 레시피를 받아와야 겠죠.
전략 패턴을 응용해서 메뉴명을 받아 오겠습니다.
// 치킨들의 레시피입니다.(전략 패턴) // 각 치킨 메뉴 클래스들이 상속받을 것입니다. interface Recipe { String menuName(); }
// 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(); }
이렇게 닭, 양념, 간장의 원산지를 가져오는 추상 메소드를 만들어 준 뒤,
국산, 수입산 클래스를 만들어 오버라이딩 해주면 됩니다.
/////////////// 수정 예정