본문 바로가기

[unity] 디자인 패턴 : 팩토리 패턴(Factory Pattern)

앤디가이 2022. 6. 30.

Unity와 C#을 통한 팩토리 패턴(심플 팩토리, 메소드 팩토리 패턴)의 정의와 사용 방법에 대해 알아보자. 

 

1. 팩토리 패턴(Factory Pattern)이란?

팩토리란 말에서 볼 수 있듯이 공장에서 생산을 잘할 수 있도록 디자인하는 패턴으로 생각하면 쉽다.

팩토리 패턴의 핵심은 객체를 생성하는 기능만 팩토리로 빼서 관리하는 것이다.

팩토리 패턴(Factory Pattern)은 크게 3가지로 구분하여 정의해 볼 수 있다.

 

심플 팩토리 패턴(Simple Factory Pattern):  패턴이라고 말하기는 애매하고 단순히 객체 생성부를 별도 클래스로 분리한 개발 방법이다.

팩토리 메소드 패턴(Factory Method Pattern) : 객체를 생성할 때 필요한 인터페이스를 만들고, 어떤 클래스의 인스턴스를 만들지는 서브 클래스가 결정하도록 하는 패턴

추상 팩토리 패턴(Abstract Factory Pattern) : 구상 클래스에 의존하지 않고도 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생산하는 인터페이스 제공을 기반으로 생성하는 패턴(요소가 바뀌는 다양한 제품군을 확장해 나갈 때 사용하면 유용함)

 

이번 블로그에서는 심플 팩토리와 팩토리 메소드 패턴을 자동차 주문 시스템을 예로 설명을 해보도록 하겠다.


2. 심플 팩토리 패턴(Simple Factory Pattern) 구현 방법

테슬라 자동차 중 Model3를 생성하는 코드를 간단히 구현해보면 다음과 같을 것이다.

    public class CarStore : MonoBehaviour
    {
        public Car OrderCar()
        {
            Car car = new Car_Model3();

            car.Prepare(); //자동차 생산 준비(부품)
            car.Assemble(); //자동차 조립
            car.Paint(); //자동차 도색
            return car;
        }
    }

 

테슬라 매장(CarStore) 클래스에서는 OrderCar함수를 통해 자동차를 생성해서 리턴해주는 작업을 수행한다.

여기서 테슬라 자동차가 Model3만 있는 건 아니니 타입을 받아 모델별 생성하는 코드를 추가해주면 다음과 같다.

using UnityEngine;

namespace FactoryPattern
{
    public class CarStore : MonoBehaviour
    {
        public Car OrderCar(CarType type)
        {
            Car car;
            switch (type)
            {
                case CarType.Model3:
                    car = new Car_Model3();
                    break;
                case CarType.ModelS:
                    car = new Car_ModelS();
                    break;
                case CarType.ModelY:
                    car = new Car_ModelY();
                    break;
                default:
                    car = new Car_Model3();
                    break;
            }

            car.Prepare(); //자동차 생산 준비(부품)
            car.Assemble(); //자동차 조립
            car.Paint(); //자동차 도색
            return car;
        }
    }
}

 

여기서 car = new Car_Model3() 를 통해 주문을 받으면 모델을 생성해주고 있다.

이제 심플 팩토리를 사용하여 객체 생성부를 클래스로 빼서 캡슐화 해주자.

SimpleCarFactory 클래스를 만든 후 객체 생성 기능만 분리해주자.

코드를 보면 다음과 같다.

using UnityEngine;

namespace FactoryPattern
{
    public class SimpleCarFactory : MonoBehaviour
    {
        public Car CreateCar(CarType type)
        {
            Car car;
            switch (type)
            {
                case CarType.Model3:
                    car = new Car_Model3();
                    break;
                case CarType.ModelS:
                    car = new Car_ModelS();
                    break;
                case CarType.ModelY:
                    car = new Car_ModelY();
                    break;
                default:
                    car = new Car_Model3();
                    break;
            }
            return car;
        }
    }
}

그럼 CarStore 클래스는 어떻게 변경되는지 확인해보자.

using UnityEngine;

namespace FactoryPattern
{
    public class CarStore : MonoBehaviour
    {
        //자동차 생산을 위한 팩토리
        SimpleCarFactory factory;

        public Car OrderCar(CarType type)
        {
            //자동차를 생성하는 부분은 팩토리에서 진행
            Car car = factory.CreateCar(CarType.Model3);

            car.Prepare(); //자동차 생산 준비(부품)
            car.Assemble(); //자동차 조립
            car.Paint(); //자동차 도색
            return car;
        }
    }
}

 

위에서 보듯 심플 팩토리는 객체 생성만 따로 캡슐화해서 클래스로 구성하는 것이 핵임이다. 패턴이라고 말하기는 어렵지만, 메소드 팩토리와 추상 팩토리의 근간이 되기 때문에 기초 지식으로 알아두면 좋다.


3. 팩토리 메소드 패턴(Factory Method Pattern) 구현 방법

위 테슬라 매장의 예를 조금 더 확장시켜 보겠다.

테슬라 자동차 매장은 각 나라 별 지점이 있을 것이며, 각 나라별로 나라 특성에 맞는 자동차 특성이 들어간 자동차가 생산될 수 있다.

(예를 들어 같은 모델이라도 유럽은 핸들이 오른쪽에 있는 자동차를 생산하고, 한국은 핸들이 왼쪽에 있는 자동차를 생산할 것이다)

그럼 CarStore를 확장해서 각 나라별 서브클래스를 만들면 구조는 다음과 같을 것이다.

 

자동차 매장 확장클래스
자동차 매장 확장클래스

중요한 부분은 CreateCar() 부분을 기존에 SimpleCarFactory에서 했던 부분을 다시 가지고 왔으며, 추상 함수로 선언하여 서브 클래스에서 구현하게 한 부분이다.

여기서 팩토리 메소드 패턴의 핵심이 나오는데 바로 인스턴스를 만드는 일을 서브 클래스에게 일임하는 것이다. 

따라서 어떤 서브클래스를 선택했느냐에 따라서 자동차의 종류가 결정된다.

 

코드로 보면 다음과 같을 것이다.

 

CarStore.cs 코드

using UnityEngine;

namespace FactoryPattern.Method
{
    public abstract class CarStore : MonoBehaviour
    {

        public Car OrderCar(CarType type)
        {
            //자동차를 생성 호출
            Car car = CreateCar(type);

            car.Prepare(); //자동차 생산 준비(부품)
            car.Assemble(); //자동차 조립
            car.Paint(); //자동차 도색
            car.Ready();
            return car;
        }

        //서브클래스에서 생성할 수 있도록 위
        public abstract Car CreateCar(CarType type);
    }
}

CarStoreForEurope.cs 코드

namespace FactoryPattern.Method
{
    public class CarStoreForEurope : CarStore
    {
        public override Car CreateCar(CarType type)
        {
            Car car;
            switch (type)
            {
                case CarType.Model3:
                    car = new Car_Model3ForEurope();
                    break;
                case CarType.ModelS:
                    car = new Car_ModelSForEurope();
                    break;
                case CarType.ModelY:
                    car = new Car_ModelYForEurope();
                    break;
                default:
                    car = new Car_Model3ForEurope();
                    break;
            }
            return car;
        }
    }
}

Car.cs 코드


using UnityEngine;

namespace FactoryPattern.Method
{
    public abstract class Car : MonoBehaviour
    {
        public void Prepare() { Debug.Log("자동차 준비 중..."); }
        public void Assemble() { Debug.Log("자동차 조립 중..."); }
        public void Paint() { Debug.Log("자동차 페인트 중..."); }
        public void Ready() { Debug.Log("자동차 생산 완료"); }
    }
}

Car_Model3ForEurope.cs 코드

using UnityEngine;

namespace FactoryPattern.Method
{    
    public class Car_Model3ForEurope : Car
    {
        public Car_Model3ForEurope()
        {
            Debug.Log("유럽형 Tesla Model 3 준비");
        }
    }

    public class Car_Model3ForKorea : Car
    {
        public Car_Model3ForKorea()
        {
            Debug.Log("한국형 Tesla Model 3 준비");
        }
    }
}

 

이제 고객 클래스에서 자동차 주문을 하는 테스트 코드를 만들어 보면 다음과 같다.

Customer.cs 코드

using UnityEngine;

namespace FactoryPattern.Method
{
    public class Customer : MonoBehaviour
    {
        void Start()
        {
            CarStore store = new CarStoreForEurope();
            Car car = store.OrderCar(CarType.Model3);
        }
    }
}

 

테스트 결과

결과 로그
결과 로그

 


4. 정리

유럽 자동차 매장에서 모델3 자동차를 주문하는 과정을 테스트 코드로 만들어 보았다.

이렇듯 팩토리 메소드 패턴을 활용하면 서브 클래스에서 인스턴스 만드는 과정이 이뤄지므로, 베이스 클래스를 따로 건드릴 필요가 없다.

미국 매장, 중국 매장에 테슬라 자동차 매장이 확장된다면, 서브클래스만 작성해주면 되기 때문에 확장성이 용이하다.(기존 베이스 클래스 코드를 건드릴 필요가 없음)

팩토리 메소드 패턴은 사용하는 서브클래스에 따라 생산되는 객체의 인스턴스가 결정되는 사실을 잘 기억하면 좋을 것 같다.

 

 

댓글