[C#, 유니티] factoryMethod와 AbstractFactory의 차이

2022. 11. 3. 19:39코딩 2막 <C#개념편>

728x90

지금까지 배웠던 내용들은 ○표시, 앞으로 배울 내용은 □표시, 시간이 된다면 배울 문법은 △표시

오늘은 C#문법 가운데 디자인패턴 중 알아두면 좋은

팩토리메소드와 추상팩토리에 대해서 배웠습니다


먼저 팩토리 메소드와 추상 팩토리과 같은 패턴들을 쉽게 분석하는 방법은 클래스UML을 보는 것입니다

오늘 배운 펙토리메서드와 추상펙토리의 ClassUML

팩토리 메서드만 먼저 보게되면 Creator라는 클래스가 product의 형태로 가지고 있고,

Product는 인터페이스라서 실체화하여 구현한 클래스이므로 자식클래스는 클래스UML을 점선으로 표시한것입니다

(abstract클래스를 상속받는 자식은 실선으로 표시)

그리고 Creator클래스가 가지고 있던 product에 팩토리메소드라는 함수를 만들어 넣어주었고

Creator라는 클래스를 상속받는 자식클래스들은 각 타입에 맞는 Product를 생성한다고 합니다

 

 그럼 이제 Player라는 Creator Class와 Weapon이라는 Product Class를 만들어서

플레이가 무기를 갖는 관계를 들어서 팩토리 메서드로 구현하겠습니다


팩토리 메서드

팩토리 메서드 패턴(Factory method pattern)은 객체지향 디자인패턴이다

Factory method는 부모클래스(상위)에 알려지지 않은 구체 클래스를 생성하는 패턴이며,

자식클래스(하위)가 어떤 객체를 생성할지를 결정하도록 하는 패턴이기도 하다

부모클래스(상위) 코드에 구체 클래스 이름을 감추기 위한 방법으로도 사용한다

 

<스크립트>

1. Weapon.cs

먼저 Weapon클래스에 추상 메서드를 선언하여 상속을 통해서 자손 클래스에서 완성하도록 유도하고자 합니다

상속을 위한 클래스이기 때문에 따로 객체를 생성할 수 없습니다

class 앞에 "abstract" 예약어를 사용하여 상속을 통해서 구현해야한다는 것을 알려주고

선언부만 작성하는 추상메서드를 선언할 수 있습니다

WEAPON_TYPE이라는 enum클래스를 만들어서 무기라는 집합으로 Sword와 Bow를 갖도록 하겠습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
 
public enum WEAPON_TYPE
{
    Sword, Bow
}
 
public abstract class Weapon : MonoBehaviour
{
    public abstract void Attack();
}
 
cs

2. Sword.cs와 Bow.cs

Weapon을 상속받는 클래스입니다Bow.cs을 보면 damage변수를 추가하였는데 이후에 Bow를 갖는 Player의 자식 클래스를 변형된 형태로 만들려고 했습니다

1
2
3
4
5
6
7
8
9
10
11
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Sword : Weapon
{
    public override void Attack()
    {
        Debug.Log("근접 공격");
    }
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Bow : Weapon
{
    public float damage;
    public override void Attack()
    {
        Debug.Log("원거리 공격");
    }
}
cs

 

3. Player.cs

플레이어는 weapon을 갖는데 처음 선언할때 null을 때립니다

그리고 weaponType을 갖는데 Swich문을 써도 되지만 if문을 써서 weapon에 넣어줄 WeaponFactory를 만듭니다

public으로 선언했기때문에 인스펙터창에서 원하는 타입을 선택할 수 있고 해당 타입이 선택되면 gameObject.AddComponent를 반환합니다

그리고 플레이어를 상속받는 클래스를 만들기위해 virtual를 해줍니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
 
public class Player : MonoBehaviour
{
    protected Weapon weapon = null;
    public WEAPON_TYPE weaponType;
 
    public virtual Weapon OnWeaponFactory()
    {
        if (weaponType == WEAPON_TYPE.Sword)
            return gameObject.AddComponent<Sword>();
        else if (weaponType == WEAPON_TYPE.Bow)
            return gameObject.AddComponent<Bow>();
 
        return null;
    }
 
    public void Start()
    {
        weapon = OnWeaponFactory();
    }
 
    public void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
            weapon.Attack();
    }
 
}
 
cs

 

4. Warrior.cs

Player를 상속받는(위 UML에서 Creator1에 해당) 전사(Warrior) 클래스를 만들었습니다

전사를 선택하면 자동적으로 sword 오브젝트가 생성되는데 이것을 프리팹화시킬 수도 있습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Warrior : Player
{
    public override Weapon OnWeaponFactory()
    {
        if (weaponType == WEAPON_TYPE.Sword)
        {
            Sword sword = gameObject.AddComponent<Sword>();
            return sword;
        }
        return null;
    }
}
cs

5. Archer.cs와 Sniper.cs

Player를 상속받는 궁수 클래스와 저격수 클래스입니다

둘다 bow를 갖지만 앞서 Bow클래스에 damage변수를 추가해주었기 때문에 이를 변형할 수 있다는 것을 보여주고자

궁수와 저격수 클래스를 만들었습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Archer : Player
{
    public override Weapon OnWeaponFactory()
    {
        if (weaponType == WEAPON_TYPE.Bow)
        {
            Bow bow = gameObject.AddComponent<Bow>();
            bow.damage = 50;
            return bow;
        }
        return null;
    }
}
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Sniper : Player
{
    public override Weapon OnWeaponFactory()
    {
        if (weaponType == WEAPON_TYPE.Bow)
        {
            Bow bow = gameObject.AddComponent<Bow>();
            bow.damage = 100;
            return bow;
        }
        return null;
    }
}
cs

<인스펙터>


 


추상 팩토리

추상 팩토리 패턴(Abstract factory pattern)은 다양한 구성 요소 별로 '객체의 집합'을 생성해야 할 때 유용하다.

이 패턴을 사용하여 상황에 알맞은 객체를 생성할 수 있다.


<스크립트>

구조적으로 추상팩터리는 팩토리메소드와 약간의 차이가 있지만 맥락은 비슷하다

 

Weapon, Sword, Bow 클래스는 팩토리메소드때와 동일하다

1. Player.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public enum CLASS_TYPE
{
    Warrior, Archer, Crusader
}
 
public class Player : MonoBehaviour
{
    Weapon weapon = null;
    Armor armor = null;
 
    public CLASS_TYPE classType;
    EquipFactory equipFactory = null;
 
    public void OnEquipFactory()
    {
        switch(classType)
        {
            case CLASS_TYPE.Warrior | CLASS_TYPE.Crusader:
                equipFactory = new WarriorEquipFactory(gameObject);
                break;
            case CLASS_TYPE.Archer:
                equipFactory = new ArcherEquipFactory(gameObject);
                break;
        }
 
        weapon = equipFactory.WeaponFactory();
        armor = equipFactory.ArmorFactory();
    }
 
    // Start is called before the first frame update
    void Start()
    {
        OnEquipFactory();
    }
 
    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.A))
        {
            weapon.Attack();
        }
    }
}
cs

2. EquipFactory.cs

EquipFactory라는 추상팩토리를 생성해서 이 클래스를 상속받는 ArmorFactory를 갖게되면

해당 Type에 맞는 Armor와 Weapon을 생성하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public interface IEquipFactory 
{
    public Weapon WeaponFactory();
    public Armor ArmorFactory();
}
 
public abstract class EquipFactory : IEquipFactory
{
    public GameObject gameObject;
    public EquipFactory(GameObject inputGameObject)
    {
        gameObject = inputGameObject;
    }
    public abstract Armor ArmorFactory();
 
    public abstract Weapon WeaponFactory();
 
}
 
 
public class WarriorEquipFactory : EquipFactory
{
    public WarriorEquipFactory(GameObject inputGameObject) : base(inputGameObject)
    {
    }
 
    public override Armor ArmorFactory()
    {
        return gameObject.AddComponent<HeavyArmor>();
    }
 
    public override Weapon WeaponFactory()
    {
        return gameObject.AddComponent<Sword>();
    }
}
public class ArcherEquipFactory : EquipFactory
{
    public ArcherEquipFactory(GameObject inputGameObject) : base(inputGameObject)
    {
    }
 
    public override Armor ArmorFactory()
    {
        return gameObject.AddComponent<LeatherArmor>();
    }
 
    public override Weapon WeaponFactory()
    {
        return gameObject.AddComponent<Bow>();
    }
}
cs

3. Armor.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public enum ARMOR_TYPE
{
    Heavy,Leather
}
public class Armor : MonoBehaviour
{
    private int defenceValue;
    public virtual int GetDefenceValue()
    {
        return defenceValue;
    }
}
cs

4. LeatherArmor.cs와 HeavyArmor.cs

Armor를 상속받는 자식 클래스


 

공감해주셔서 감사합니다

728x90