팩토리 메소드 패턴 정의
=> 객체를 생성하기 위한 인터페이스를 정의하는데, 서브클래스에서 어떤 클래스를 만들지를 결정하게 함으로써 객체 생성을 캡슐화 한다.
=> 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기게 된다.
서브클래스가 만든다는 뜻
=> 생산자 클래스가 실제 생산될 제품에 대한 사전지식을 알고 싶어하지 않고, 사용하고만 싶어한다.
=> 그래서 객체의 생성을 서브클래스에게 위임하고, 서브클래스에서는 어떤 클래스의 인스턴스를 만들지 결정한다.
사용목적 및 용도, 장점
=> 객체 생성 코드를 전부 한 객체 또는 메소드에 넣으면 코드에서 중복되는 내용을 제거할 수 있고, 객체의 생성을 한 군데에서만 관리하면 된다.
=> 동일한 인터페이스 구현으로 새로운 객체가 추가되어도 소스의 수정이 거의 없다.
=> 구현이 아닌 인터페이스 바탕의 프로그래밍이 되고, 그 결과 유연성과 확장성이 뛰어난 코드가 된다.
예를들어, 3개의 클래스가 있다고 해보자.
UseFactoryMethod 클래스 : 타입 별로 생성
MonsterAGenerator 클래스 : 팩토리 메서드를 통해 몬스터A 여러마리 생성
MonsterBGenerator 클래스 : 팩토리 메서드를 통해 몬스터B 여러마리 생성
=> 몬스터A가 더 많이 생성되어야 한다면 MonsterAGenerator를 수정 / UseFactoryMethod는 수정할 필요가 없다.
=> 몬스터A와 B를 동시 출현하고 싶다면 MonsterAGenerator나 MonsterBGenerator를 수정
=> 몬스터C를 추가하고 싶다면 MonsterCGenerator를 만들고 UseFactoryMethod에 추가한다.
실제로 유니티에서 로그를 통해 확인해보자. (로그 부분을 바꿔 오브젝트 생성이나 기능 구현으로 쓸 수 있다.)
먼저 프로젝트 정보다.
로그로만 확인하려 해서 씬과 스크립트로만 진행할 예정이다.
// Monster.cs
public enum MonsterType
{
Goblin,
Slime
}
abstract class Monster
{
protected MonsterType type;
protected string name;
protected int hp;
protected int damage;
public abstract void Attack();
}
먼저 Monster.cs다.
추상클래스로, 각종 멤버 변수와 Attack이라는 추상 메소드를 가지고 있다.
// Slime.cs
using UnityEngine;
class Slime : Monster
{
public Slime()
{
type = MonsterType.Slime;
name = "Slime";
hp = 100;
damage = 10;
Debug.Log(this.name + " 생성");
}
public override void Attack()
{
Debug.Log(this.name + " - " + this.damage + "데미지 공격");
}
}
// Goblin.cs
using UnityEngine;
class Goblin : Monster
{
public Goblin()
{
type = MonsterType.Goblin;
name = "Goblin";
hp = 200;
damage = 20;
Debug.Log(this.name + " 생성");
}
public override void Attack()
{
Debug.Log(this.name + " - " + this.damage + "데미지 공격");
}
}
다음은 Monster클래스를 상속받은 Slime과 Goblin이다.
생성자에서 이름과 hp, damage를 세팅하고 추상 메소드를 override해서 Attack메소드를 구현했다.
이제 이런 몬스터들을 생성해야 하는데
생성의 효율성을 위해 팩토리를 이용해 생성하는 것이다.
// MonsterGenerator.cs
using System.Collections.Generic;
abstract class MonsterGenerator
{
public List<Monster> monsters = new List<Monster>();
public List<Monster> getMonsters()
{
return monsters;
}
// Factory Method
public abstract void CreateMonsters();
}
팩토리를 만들기 위한 추상 클래스 MonsterGenerator이다.
CreateMonsters라는 추상 메소드를 가지고 있다.
// PatternGenerator.cs
class PatternAGenerator : MonsterGenerator
{
public override void CreateMonsters()
{
monsters.Add(new Slime());
monsters.Add(new Slime());
monsters.Add(new Slime());
}
}
class PatternBGenerator : MonsterGenerator
{
public override void CreateMonsters()
{
monsters.Add(new Goblin());
monsters.Add(new Goblin());
monsters.Add(new Goblin());
}
}
위의 추상 클래스를 구현한 구상 클래스이다.
// UseFactoryMethod.cs
using System.Collections.Generic;
using UnityEngine;
public class UseFactoryMethod : MonoBehaviour
{
MonsterGenerator[] monsterGenerators = null;
void Start()
{
monsterGenerators = new MonsterGenerator[2];
monsterGenerators[0] = new PatternAGenerator();
monsterGenerators[1] = new PatternBGenerator();
}
public void MakeGoblin()
{
monsterGenerators[0].CreateMonsters();
List<Monster> monsters = monsterGenerators[0].getMonsters();
foreach(Monster monster in monsters)
{
monster.Attack();
}
}
public void MakeSlime()
{
monsterGenerators[1].CreateMonsters();
List<Monster> monsters = monsterGenerators[1].getMonsters();
foreach (Monster monster in monsters)
{
monster.Attack();
}
}
}
마지막으로 UseFactoryMethod이다.
MonsterGenerator를 배열로 만들고 자식을 요소로 넣어준 뒤 만드는 방식이다.
위는 테스트를 위한 버튼에 연결하기 위해 메소드를 나누어 놓았지만
Start나 원하는 위치에서 실행시켜도 무방하다.
실행 후 로그의 모습이다.
Slime 3마리 생성 후 공격, Goblin 3마리 생성 후 공격 전부 잘 동작한다.
만약 위에서 Slime이나 Goblin을 5마리씩 생성하고 싶다면
UseFactoryMethod.cs는 건드릴 필요가 없다.
PatternGenerator로 들어가 갯수만 늘리면 된다.
또, Slime과 Goblin을 같이 나오게 한다거나 새로운 몬스터가 추가되었을 때도
PatternGenerator에서 클래스를 추가하고 UseFactoryMethod에도 추가만 해주면 된다.
(새로운 객체가 추가되어도 코드의 수정이 거의 없다.)
'프로그래밍 > 디자인 패턴' 카테고리의 다른 글
빌더 패턴 (Builder Pattern) (0) | 2020.05.31 |
---|---|
추상 팩토리 패턴 (Abstract Factory Pattern) (0) | 2020.05.31 |
심플 팩토리 패턴 (Simple Factory Pattern) (0) | 2020.05.24 |
스트레티지 패턴 (Strategy Pattern) (0) | 2020.05.20 |
싱글턴 패턴 (Singleton Pattern) (0) | 2020.05.19 |