Chapter 5. 팩토리 메서드 패턴(Factory Method Pattern)
카테고리: Design Pattern
인프런에 있는 이재환님의 강의 게임 디자인 패턴 with Unity 를 듣고 정리한 필기입니다. 😀
Chapter 5. Factory Method Pattern
🔔 Factory Method Pattern
생성하는 공장은 딱 하나. 그러나 어떻게 생성할지에 대한 여러 방식들을 공장의 자식 클래스들로 구현.
- 다양한 자식들을 생성하고 리턴하는 팩토리 클래스는 딱 하나만 두고
- 어떤 방식으로 몇개 생성할지 등등 이런 어떤식으로 생성할지에 관한 생성의 형태는 팩토리 클래스를 상속하는 서브 팩토리 클래스들에게 맡긴다.
- 생성 형태는 여러가지가 있으므로 서브 팩토리 클래스들은 여러개가 있을 수 있다.
- 어떤 방식으로 몇개 생성할지 등등 이런 어떤식으로 생성할지에 관한 생성의 형태는 팩토리 클래스를 상속하는 서브 팩토리 클래스들에게 맡긴다.
- 오브젝트 생산 공장 👉 팩토리 클래스 (부모)
- 오브젝트의 여러가지 생산 방식들 👉 서브 팩토리 클래스 (자식들)
부모인 팩토리 클래스로 서브 팩토리 클래스들을 참조하면, 팩토리 클래스 입장에선 뭘 어떻게 생성하는지 알 필요 없이 그냥 생성만 해주면 된다. 👉
다형성
- 유지 보수가 편리
- 생성 방식들을 추가/수정할땐 서브 팩토리 클래스 내용을 수정하거나 서브 팩토리 클래스 또 다른거 하나 더 추가하거나 그렇게만 하면 된다.
- 팩토리 클래스를 수정할 필요는 없다.
🔔 예제 1
- 📜Unit 추상 클래스
- 📜Marine 상속
- Attack() 오버라이딩
- 📜Firebat 상속
- Attack() 오버라이딩
- 📜Marine 상속
- 🧾UnitGenerator 추상 클래스
- 팩토리 클래스
- Unit (marine, firebat) 들을 생성하고 리스트에 담는다.
- 생산방식이 A,B 두가지가 있다는 것만 알지 구체적인 생산 방식은 모른다. 얘 입장에선 몰라도 됨. 그저 CreateUnits()만 해주면 됨.
- 🧾PatternAGenerator 상속
- 팩토리 서브 클래스 2개
- 🧾PatternGenerator_A
- 마린 8개 만드는 생산 방식.
- CreateUnits() 오버라이딩 👉 팩토리 메서드 패턴
- 🧾PatternGenerator_B
- 파이어뱃 3개, 마린 5개 만드는 생산 방식.
- CreateUnits() 오버라이딩 👉 팩토리 메서드 패턴
- 🧾PatternGenerator_A
- 팩토리 서브 클래스 2개
- 팩토리 클래스
- 📜UseFactoryMethod
- 팩토리 클래스인 🧾UnitGenerator 타입의 크기 2 배열을 선언하고
- 이에 팩토리 서브 클래스인 🧾PatternGenerator_A, 🧾PatternGenerator_B 타입 객체를 생성하고 넣어 준다.
다형성 1
: 부모인 팩토리 클래스 타입으로 자식들인 각각 두가지의 생산 방식을 나타내는 A, B를 각각 참조하여 각자의 생산 방식대로 Unit 들을 생성해줌.CreateUnits
는 추상 함수.
다형성 2
: 부모인 Unit 타입으로 자식들인 marine, firebat를 각각 참조하여 각자의 방식대로 Attack() 수행Attack
는 추상 함수.
- 팩토리 클래스인 🧾UnitGenerator 타입의 크기 2 배열을 선언하고
Unit
📜Unit.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum UnitType
{
Marine,
Firebat
}
abstract class Unit
{
protected UnitType type;
protected string name;
protected int hp;
protected int exp;
public abstract void Attack();
}
📜Firebat.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class Firebat : Unit
{
public Firebat()
{
type = UnitType.Firebat;
name = "Firebat";
hp = 120;
exp = 15;
Debug.Log (this.name + " : 생성!!");
}
public override void Attack()
{
Debug.Log(this.name + " : 공격!!");
}
}
📜Marine.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class Marine : Unit
{
public Marine()
{
type = UnitType.Marine;
name = "Marine";
hp = 100;
exp = 50;
Debug.Log (this.name + " : 생성!!");
}
public override void Attack()
{
Debug.Log(this.name + " : 공격!!");
}
}
팩토리 메서드 패턴
📜UnitGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
abstract class UnitGenerator
{
public List<Unit> units = new List<Unit>();
public List<Unit> getUnits()
{
return units;
}
// Factory Method
public abstract void CreateUnits();
}
📜PatternGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class PatternGenerator_A : UnitGenerator
{
public override void CreateUnits()
{
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
}
}
class PatternGenerator_B : UnitGenerator
{
public override void CreateUnits()
{
units.Add(new Firebat());
units.Add(new Firebat());
units.Add(new Firebat());
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
units.Add(new Marine());
}
}
팩토리 메서드 패턴의 사용
📜UnitFacotryMethod
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UseFactoryMethod : MonoBehaviour {
UnitGenerator[] unitGenerators = null;
void Start () {
unitGenerators = new UnitGenerator[2];
unitGenerators[0] = new PatternGenerator_A();
unitGenerators[1] = new PatternGenerator_B();
}
public void DoMakeTypeA()
{
unitGenerators[0].CreateUnits();
List<Unit> units = unitGenerators[0].getUnits();
foreach (Unit unit in units)
{
unit.Attack();
}
}
public void DoMakeTypeB()
{
unitGenerators[1].CreateUnits();
List<Unit> units = unitGenerators[1].getUnits();
foreach (Unit unit in units)
{
unit.Attack();
}
}
}
🔔 예제 2
Database 종류마다 접속 방식이 다르다
- 접속 기능을 수행하는 함수는 가상(추상)함수여야 한다.
- Database 자식들마다 각자 알아서 오버라이딩 하도록.
ConnectDatabase ()
함수
- Database 자식들마다 각자 알아서 오버라이딩 하도록.
Database 종류마다 삽입과 선택 연산은 같다
- 부모인 Database에만 구현해두고 상속받게만 하면 됨. 재정의 필요 없이.
InsertData ()
함수SelectData ()
함수
구조
- 📜Database 추상 클래스
- 📜MySQL 상속
- ConnectDatabase () 오버라이딩
- 📜Oracle 상속
- ConnectDatabase () 오버라이딩
- 📜Informix 상속
- ConnectDatabase () 오버라이딩
- 📜MySQL 상속
- 🧾DatabaseFactory 추상 클래스
- 팩토리 클래스
Database
(Oracle, MySQL, Informix) 들을 생성하고 리턴한다.- 어떤 데이터 베이스를 생성해야할지는 모른다. 얘 입장에선 몰라도 됨. 그저 Database (Oracle, MySQL, Informix) 들을 생성하고 리턴만 해주면 됨.
- 🧾DatabaseGenerator 상속
- 팩토리 서브 클래스
- ⭐어떤 종류의 Database를 생성할지는 얘가 정한다.
- 종류 추가 수정시 얘만 수정해주면 됨.
- 슬롯에 들어가는 Database 종류 오브젝트에 따라 다르게 리턴된다.
- MakeDatabase() 오버라이딩 👉 팩토리 메서드 패턴
- ⭐어떤 종류의 Database를 생성할지는 얘가 정한다.
- 팩토리 서브 클래스
- 팩토리 클래스
- 📜UseFactoryMethod
다형성1
- DatabaseFactory factory = GetComponent<DatabaseGenerator>();
다형성2
- Database db = factory.MakeDatabase();
Database
📜Product.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Database
{
public string name;
public double rows;
// 데이터베이스마다 접속 방식이 다르다.
public abstract void ConnectDatabase ();
// 표준 SQL문을 사용한다. (업무처리 방식이 같다.)
public void InsertData ()
{
Debug.Log (name + " 에 데이터를 추가했습니다..");
}
public void SelectData ()
{
Debug.Log (name + " 에서 데이터를 " + rows + "게 조회했습니다.");
}
}
public class MySQL : Database
{
public MySQL()
{
name = "MySQL";
rows = 20;
}
public override void ConnectDatabase ()
{
Debug.Log (name + " 에 접속했습니다..");
}
}
public class Oracle : Database
{
public Oracle()
{
name = "Oracle";
rows = 10;
}
public override void ConnectDatabase ()
{
Debug.Log (name + " 에 접속했습니다..");
}
}
public class Informix : Database
{
public Informix()
{
name = "Informix";
rows = 40;
}
public override void ConnectDatabase ()
{
Debug.Log (name + " 에 접속했습니다..");
}
}
팩토리 메서드 패턴
📜DatabaseFactory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum DBType
{
MySQL,
Oracle,
Informix
};
public abstract class DatabaseFactory : MonoBehaviour
{
// Factory Method
public abstract Database MakeDatabase();
}
📜DatabaseGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DatabaseGenerator : DatabaseFactory
{
// 데이터베이스 변경시 여기서 타입을 변경한다.
public DBType dbType = DBType.MySQL;
public override Database MakeDatabase()
{
// 회사 사정에 의해 어떤 데이터베이스를 다시 사용할지 모른다.
// 그래서 구축한 정보를 지우지 않고 재사용시를 대비한다.
if (dbType == DBType.MySQL)
{
Debug.Log("MySQL use...");
return new MySQL();
}
else if (dbType == DBType.Oracle)
{
Debug.Log("Oralce use...");
return new Oracle();
}
else if (dbType == DBType.Informix)
{
Debug.Log("Informix use...");
return new Informix();
}
else
{
return null;
}
}
}
팩토리 메서드 패턴의 사용
📜UnitFacotryMethod
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UseFactoryMethod : MonoBehaviour {
DatabaseFactory factory = null;
Database db = null;
void Start ()
{
factory = GetComponent<DatabaseGenerator>();
// 어떤 데이터베이스가 사용될지 여기서는 모른다.
// 팩토리 메서드에서 제공되는 데이터베이스를 그냥 사용한다.
db = factory.MakeDatabase();
// ConnectDatabase()는 추상 함수 이므로 데이터베이스 종류에 따라 다르게 실행된다.
db.ConnectDatabase();
// 부모인 DataBase에서 정의한 내용대로 실행된다. 즉 모든 데이터베이스가 공통적인 내용으로 실행된다.
db.InsertData();
db.SelectData();
}
}
🔔 예제 3
구조
- 📜Boss 추상 클래스
- 📜EventBoss 상속
- 이벤트 기간에만 등장하는 보스몹
- Attack() 오버라이딩
- 📜NormalBoss 상속
- 평소 보스 몹
- Attack() 오버라이딩
- 📜EventBoss 상속
- 🧾BossFactory 추상 클래스
- 팩토리 클래스
Boss
(EventBoss, NormalBoss) 들을 생성한다- 어떤 보스를 생성해야할지는 모른다. 얘 입장에선 몰라도 됨. 그저 Boss (EventBoss, NormalBoss)들을 생성하고 리턴만 해주면 됨.
- 🧾BossGenerator 상속
- 팩토리 서브 클래스
- ⭐어떤 종류의 Boss를 생성할지는 얘가 정한다.
- 종류 추가 수정시 얘만 수정해주면 됨.
- 슬롯에 들어가는 Boss 종류 오브젝트에 따라 다르게 리턴된다.
- CreateBoss(Transform tran) 오버라이딩 👉 팩토리 메서드 패턴
- 보스 종류에 따라
- 보스 프리팹을 오브젝트로 생성하여 찍어내고
- 보스의 위치를 인수로 들어온 위치로.
- 보스의 회전값을 인수로 들어온 회전값으로.
- 보스 종류에 따라
- ⭐어떤 종류의 Boss를 생성할지는 얘가 정한다.
- 팩토리 서브 클래스
- 팩토리 클래스
- 📜UseFactoryMethod
다형성1
- BossGenerator factory = GetComponent<BossGenerator>();
- factory.CreateBoss(tran);
Boss
📜Boss.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum BossType
{
NormalBoss,
EventBoss
}
abstract class Boss : MonoBehaviour
{
protected BossType type;
protected int hp;
protected int exp;
public abstract void Attack();
}
📜EventBoss.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class EventBoss : Boss
{
void Start()
{
type = BossType.EventBoss;
hp = 200;
exp = 20;
name = "Event Boss";
Debug.Log (this.name + " : 출현!!");
}
public override void Attack()
{
Debug.Log(this.name + " : 공격!!");
}
}
📜NormalBoss.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class NormalBoss : Boss
{
void Start()
{
type = BossType.NormalBoss;
hp = 200;
exp = 15;
name = "Normal Boss";
Debug.Log (this.name + " : 출현!!");
}
public override void Attack()
{
Debug.Log(this.name + " : 공격!!");
}
}
팩토리 메서드 패턴
📜BossFactory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
abstract class BossFactory : MonoBehaviour
{
// Factory Method
public abstract void CreateBoss(Transform tran);
}
📜BossGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class BossGenerator : BossFactory
{
// 설날 코스튬 보스몹 지정
public BossType type = BossType.EventBoss;
public GameObject _normalBoss;
public GameObject _eventBoss;
public override void CreateBoss(Transform tran)
{
if (type == BossType.NormalBoss)
{
GameObject boss = Instantiate(_normalBoss) as GameObject;
boss.transform.position = tran.position;
boss.transform.localRotation = tran.localRotation;
}
else if (type == BossType.EventBoss)
{
GameObject boss = Instantiate(_eventBoss) as GameObject;
boss.transform.position = tran.position;
boss.transform.localRotation = tran.localRotation;
}
}
}
팩토리 메서드 패턴의 사용
📜UnitFacotryMethod
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UseFactoryMethod : MonoBehaviour {
BossGenerator factory = null;
public Text desc;
void Start () {
//factory = new BossGenerator();
factory = GetComponent<BossGenerator>();
if (factory.type == BossType.NormalBoss)
{
desc.text = "Normal Boss";
}
else if (factory.type == BossType.EventBoss)
{
desc.text = "Event Boss";
}
// 로직에 따라 특정 위치 지정
Transform tran = this.gameObject.transform;
// 무엇이 만들어질지 여기서는 모른다.
// 이벤트 기간에 맞춰 팩토리 클래스에서 타입이 변경되었다면
// 해당 보스가 등장하게 된다.
factory.CreateBoss(tran);
}
}
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글남기기