HaPpY hApPy

FactoryPattern 팩토리 패턴 본문

DesignPattern (디자인패턴)/Head First Design Pattern

FactoryPattern 팩토리 패턴

juniguya 2013. 7. 27. 15:57

[2013.10.26 수정]

일단 팩토리 패턴은 크게 팩토리 패턴, 팩토리 메소드 패턴, 추상 팩토리 패턴 (뒤로 갈수록 복잡하다)

Factory Pattern 은 너무 단순해서 그냥 거의 리팩토링 수준으로 패턴이라 하기 그렇다,

단순히 객체를 생성하는 부분을 한곳에 모아서 관리를 용이하게 한다.


class CPizzaStore

    {

        private CSimplePizzaFactory m_factory;

 

        public CPizzaStore(CSimplePizzaFactory factory)

        {

            m_factory = factory;

        }

      

        public CPizza Order(string type)

        {

            CPizza pizza;

            pizza = m_factory.CreatePizza(type);

 

            pizza.Prepare();

            pizza.Bake();

            pizza.Cut();

            pizza.Box();

 

            return pizza;

        }

    }

 


public class CSimplePizzaFactory

    {

        public CPizza CreatePizza(string type)

        {

            CPizza pizza = null;

 

            switch(type)

            {

                case "cheese":

                    pizza = new CCheesePizza();

                    break;

                case "pepperoni":

                    pizza = new CPepperoniPizza();

                    break;

                case "clam":

                    pizza = new CClamPizza();

                    break;

                case "veggie":

                    pizza = new CVeggiePizza();

                    break;

            }

 

            return pizza;

        }

    }

 

팩토리 메소드 패턴은 만약 저 피자 가계를 여러군데서 연다고 가정했을때 CPizzaStore을 추상 클래스로 만들고 피자 만드는 함수 도한 추상화 하여 이를 상속받는 가계에서 CreatePizza(string item)을 override해서 정의한다.


abstract class CPizzaStore // 부분을 추상화 하고

    {

        public abstract CPizza CreatePizza(string type); // 피자 만드는 함수를 추상화

 

        public CPizza OrderPizza(string type)

        {

            CPizza pizza;

            pizza = CreatePizza(type); // 호출을 추상화 한다.

 

            pizza.Prepare();

            pizza.Bake();

            pizza.Cut();

            pizza.Box();

 

            return pizza;

        }

    }


class CNYPizzaStore : CPizzaStore

    {

        public override CPizza CreatePizza(string item)

        {

            CPizza pizza = null;

 

            switch (item)

            {

                case "cheese":

                    pizza = new CNYStyleCheesePizza();

                    break;

                case "pepperoni":

                    pizza = new CNYStyleVeggiePizza();

                    break;

                case "clam":

                    pizza = new CNYStyleClamPizza();

                    break;

                case "veggie":

                    pizza = new CNYStylePepperoniPizza();

                    break;

            }

 

            return pizza;

        }

    }

 

Main에서 아래와 같이 호출가능 (CChicagoPizzaStoreclass를 만들었다는 전제하에)

  CPizzaStore nyStore = new CNYPizzaStore();

            CPizzaStore chStore = new CChicagoPizzaStore();

 

            CPizza nyPizza = nyStore.OrderPizza("cheese");

            CPizza chPizza = chStore.OrderPizza("cheese");

 

마지막 으로 추상 팩토리 패턴은 아직 이해가 잘가지 않는다.

다음에 시간날때 정리해야겠다.

일단 팩토리 메소드 패턴과 다른점은 팩토리 메소드 패턴은 NYPizzaStore와 ChicagoPizzaStore에서 

각각 NYCheesePizza, ChicagoCheesePizza 등과 같이 같은 종류의 피자를 가계만 다른데서 만든다.

이말은 가계가 10개면 CheesePizza도 10개씩 있어야 한다. 

이부분을 Interfece를 이용해서 CheesePizza는 하나만 있고 안에 들어가는 재료(Ingredient)을 바꿔서 구현하는게

추상 팩토리 패턴이다.


------------------------------원문------------------------------------------------------------------------

객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지를 서브 클래스에서 결정하게 만든다.

즉, 이 패턴을 이용하면 어떤 클래스를 만들지 서브 클래스에게 맡기는 것이고,

서브 클래스에 맡긴다는건 컴파일 타임이 아닌, 런타임에 변경할수가 있다.(음,,ㅡㅡ억지로 끼어맞추는 느낌 ㅋ)


Head First Design Pattern 에서는 피자 가계를 예를 들고있다.


FactoryPattern.zip



피자 가계마다(뉴욕,시카고,등등) 이름은 같지만 들어가는 토핑이나 굽는시간, 자르는 모양등이 다르게 정의할때 유용함.


가계별로 피자 종류 enum

namespace FactoryPattern
{
    public enum PizzaType
    {
        CHEESE,
        VEGGIE,
        CLAM,
        PEPPERONI
    }
}

먼저 각종 피자들이 상속할 베이스 Pizza class

namespace FactoryPattern
{
    public abstract class Pizza
    {
        protected string name;
        protected string dough;
        protected string sauce;
        protected List toppings;

        public Pizza()
        {
            toppings = new List();
        }

        public void Prepare()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("Prepareing " + name + "\n");
            sb.Append("Tossing " + dough + "\n");
            sb.Append("Adding " + sauce + "\n");
            sb.Append("Adding toppings:" + "\n");

            foreach (string topping in toppings)
            {
                sb.Append("\t" + topping + "\n");
            }
            Console.WriteLine(sb.ToString());
        }

        public virtual void Bake()
        {
            Console.WriteLine("Bake for 25 minutes at 350");
        }

        public virtual void Cut()
        {
            Console.WriteLine("Cutting the pizza into diagonal slices");
        }

        public virtual void Box()
        {
            Console.WriteLine("Place pizza in official PizzaStore box");
        }

        public string GetName()
        {
            return name;
        }
    }
}

딱히 특별할게 없는것 같다,,,일반 추상클래스와 다를게 없당.

위 클래스를 상속하는 뉴욕 스타일 치즈 피자와, 시카고 스타일 치즈 피자,

(나머지 3종류 피자는 구현하지 않았다.)

namespace FactoryPattern
{
    class NYStyleCheesePizza : Pizza
    {
        public NYStyleCheesePizza() //뉴욕 피자
		{
			name = "NY Style Awe Some! Cheese Pizza";
			dough = "Extra Thin Good Dough";
			sauce = "Plum Apple Sauce";

			toppings.Add("Ham Cheese Tomato");
		}
    }

    class ChicagoStyleCheesePizza : Pizza
    {
        public ChicagoStyleCheesePizza()
		{
			name = "Chicago Style Deep Dish Cheese Pizza";
			dough = "Extra Thick Crust Dough";
			sauce = "Plum Tomato Sauce";

			toppings.Add("Shredded Mozzarella Cheese");
		}

        public override void Cut() //시카고 피자는 자르는 모양이 달라서 Cut()을 override했다.
        {
            //return base.Cut();
            Console.WriteLine("Cutting the pizza into square slices \n");
        }
    }
}

이제 이 패턴의 핵심 PizzaStore class다

namespace FactoryPattern
{
    public abstract class PizzaStore
    {
        public PizzaStore()
        {
        }

        public Pizza OrderPizza(PizzaType type)
        {
            Pizza pizza;

            //이게 이 패턴의 핵심이다.
            pizza = CreatePizza(type); //객체 타입을 서브 클래스에서 정의한다. 
           

            //서브 클래스에서 정의된 데로 여기서 결과가  달라진다.
            pizza.Prepare();
            pizza.Bake();
            pizza.Cut();
            pizza.Box();

            return pizza;
        }
        
        protected abstract Pizza CreatePizza(PizzaType type); //각각의 피자 스토어 마다 CreatePizza를 override 해서 각 가계에 맞는 피자를 만든다.
    }
}

위에 CreatePizza에 보면 피자 타입에 따라 서브 클래스에서 컴파일 타임이 아닌 런타임에 서브 클래스에서 객체가 정해진다.

위 PizzaStore 클래스를 상속하는 NY Store와 Chicago Store는 크게 다를게 없다.

namespace FactoryPattern
{
    class NYPizzaStore : PizzaStore
    {
        public NYPizzaStore()
        {}

        protected override Pizza CreatePizza(PizzaType type)
        {
            switch (type) //PizzaType에 따라 런타임에 객체를 생성해서 reference를 넘겨준다.
            {
                case PizzaType.CHEESE://치즈 피자일때
                    return new NYStyleCheesePizza(); //뉴욕 피자 스토어에서(마지막에 있는 메인 함수참조)  Cheese 를 선택했으니 NY 스타일 치즈피자 객체의 레프런스 리턴!
                case PizzaType.CLAM: //나머지 3개의 피자는 따로 구현하지 않았다.
                case PizzaType.PEPPERONI:
                case PizzaType.VEGGIE:
                default:
                    return null;
            }
        }
    }

    class ChicagoStore : PizzaStore
    {
        public ChicagoStore()
        { }

        protected override Pizza CreatePizza(PizzaType type)
        {
            switch (type) //PizzaType에 따라 런타임에 객체를 생성해서 reference를 넘겨준다.
            {
                case PizzaType.CHEESE: //같은 치즈지만
                    return new ChicagoStyleCheesePizza();//Chicago 피자 스토어에서(마지막에 있는 메인 함수참조)  Cheese 를 선택했으니 Chicago 스타일 치즈피자 객체의 레프런스 리턴!
                case PizzaType.CLAM: //나머지 3개의 피자는 따로 구현하지 않았다.
                case PizzaType.PEPPERONI:
                case PizzaType.VEGGIE:
                default:
                    return null;
            }
        }
    }
}

이제 메인 

namespace FactoryPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            PizzaStore Store = new NYPizzaStore();//NYPizzaStore
            Pizza pizza = Store.OrderPizza(PizzaType.CHEESE); //NYStore 의 Cheese 스타일로 만들것이고
            Console.WriteLine("Ethan who lives in NY is ordering a " + pizza.GetName() + "\n");

            Store = new ChicagoStore();//ChicagoStore
            pizza = Store.OrderPizza(PizzaType.CHEESE); //ChicagoStore 의 Cheese 스타일로 만들것이고
            Console.WriteLine("Jun who lives in Chicago is ordering a " + pizza.GetName() + "\n");
        }
    }
}