본문 바로가기

Unity/Study

Unity C#) 디자인 패턴 가이드 #2. 팩토리 패턴(Factory pattern)

본 글은 유니티에서 발간한 전자책 'Level up your programming with game programming patterns'을 정리한 글입니다. 원문은 다음 링크에서 확인하실 수 있습니다.

https://unity.com/kr/resources/level-up-your-code-with-game-programming-patterns

 

 

1. FACTORY PATTERN

 

많은 게임들이 게임 플레이 중에 다양한 것들을 생성한다. 이때 오브젝트들을 생성하는 특별한 오브젝트를 두는 것이 유용할 수 있다. 팩토리 패턴(factory pattern)은 이를 위해 'factory(공장)'이라는 특별한 오브젝트를 지정하며, 공장의 'product(제품)'들을 생성하는 데 관련된 세부 사항들을 캡슐화한다.

 

팩토리 패턴 도식

 

팩토리 패턴에서 product들이 공통적인 인터페이스나 기초 클래스를 따르게 하여 factory와 독립적인 product 만의 생성 로직을 갖게 할 수 있다. 이를 통해 새로운 오브젝트들을 생성하는 것이 더 확장 가능하게 된다.

 

또한 factory를 하위 클래스화해 적, 장애물 등의 특정 product를 담당하는 factory를 여러 개 만들 수도 있다.

 

다음의 예시를 통해 이를 확인할 수 있다.

 

 

2. Example: A simple factory

 

팩토리 패턴에서 product를 생성할 때 프리팹을 통해 오브젝트를 생성할 수 있다. 거기에 더해 product의 커스텀 동작을 실행시키고 싶을 수도 있을 것이다.

 

그러한 로직을 위해 if문이나 switch문을 이용할 수도 있지만, 대신에 다음과 같이 인터페이스 IProduct와 추상 클래스 Factory를 만들어보자.

 

 

이는 다음과 같은 구조를 낳을 수 있다.

 

간단한 팩토리 패턴 구조

 

IProduct 인터페이스는 product들 사이의 공통적인 사항들을 정의한다. 예시의 경우 간단하게 ProductName 프로퍼티와 Initialize 메서드이다. 이러한 인터페이스를 따르는 한해서 ProductA, ProductB와 같이 product들을 원하는만큼 정의할 수 있다.

 

기초 클래스 Factory는 IProduct를 반환하는 메서드 GetProduct를 가진다. 이는 추상 클래스이기 때문에 Factory의 객체를 직접적으로 만들 수 없으며, ConcreteFactoryA, ConcreteFactoryB와 같이 구상 클래스(concrete class)를 파생시켜야 한다.

 

이 예시에서 GetProduct는 Vector3의 위치를 받아 특정 위치에 프리팹 오브젝트를 생성되도록 하고, 각 구상 팩토리에서 그에 맞는 프리팹이 저장되도록 할 것이다.

 

ProductA와 ConcreteFactoryA의 다음과 같이 작성할 수 있을 것이다.

 

 

각 product의 클래스들은 고유한 버전의 Initialize를 가진다. 예시에서 ProductA는 Initialize에서 파티클시스템을 플레이하며, 이는 ConcreteFactoryA가 product를 생성할 때 실행된다. factory(ConcreteFactoryA)는 이 파티클시스템을 플레이하는 것과 관련된 어떠한 로직도 포함하지 않으며, 그저 모든 product들에 공통적인 Initialize를 호출할 뿐이다.

 

 

3. Pros and cons

 

팩토리 패턴의 진가는 많은 product 타입을 세팅할 때 발휘된다. 새로운 product의 타입을 정의하는 것이 기존의 product들에 영향을 주거나 기존 코드의 수정을 요구하지 않으며,.factory가 product의 내부 로직을 알 필요 없이 Initialize만 호출하면 되므로 factory의 코드를 비교적 짧게 유지할 수 있다.

 

그러나 팩토리 패턴을 구현하기 위해 많은 클래스들과 하위 클래스들을 만들어야 하며, 이는 오버헤드를 발생시킬 수 있다.

 

 

4. Improvements

 

예시에서 보여진 것 이외에도 팩토리 패턴의 다양한 구현이 있을 수 있다. 자신만의 팩토리 패턴을 설계하기 위해 다음과 같은 조정 사항을 고려해 볼 수 있다.

 

- product들의 서치를 위해 딕셔너리를 사용할 수 있다.

이름과 아이디 등의 고유한 식별자를 key로, product의 타입을 value로 저장할 수 있다. 이렇게 하면 product들이나 그에 맞는 factory들을 찾는 것이 더 편리해질 수 있다.

 

- factory 혹은 factory manager을 static으로 만들 수 있다.

이것이 더 편리할 수 있지만, static 클래스들은 인스펙터에 나타나지 않으므로 product들의 집합을 static으로 두는 등의 작업이 더 필요할 것이다.

 

- 게임 오브젝트가 아닌 것들이나 MonoBehaviour가 아닌 것들에도 적용할 수 있다.

product를 프리팹이나 다른 유니티에서 제공하는 것들에 제한하지 마라. 팩토리 패턴은 뿐만 아니라 어떤 C# 객체에도 적용될 수 있다.

 

- 오브젝트 풀 패턴과 함께 사용할 수 있다.

factory는 반드시 새로운 오브젝트를 '생성'할 필요는 없다. 하이라키에서 존재하는 오브젝트를 탐색할 수도 있으며, 많은 오브젝트들을 생성할 때에는 메모리 관리를 위해 오브젝트 풀 패턴을 사용할 수도 있다.

 

 

factory들은 필요에 따라 어떤 게임 플레이 요소도 생성할 수 있다. 가령 팩토리 패턴은 단순히 제품을 생성하는 것을 넘어서 대화창의 UI를 세팅하는 것과 같이 큰 작업의 일부로도 사용될 수 있다.