Post

디자인 패턴(Design Patterns)이란? - 객체지향 설계의 검증된 해법

디자인 패턴(Design Patterns)이란?

📌 학습 목표

  • 디자인 패턴의 개념과 GOF 23개 패턴 이해
  • 게임 개발에서 자주 사용되는 패턴들 파악
  • 각 패턴의 장단점과 적절한 사용 시점 습득
  • 실제 게임 프로젝트에서의 패턴 적용 사례 학습

📌 정의

디자인 패턴(Design Patterns)은 소프트웨어 설계에서 반복적으로 발생하는 문제들에 대한 검증된 해결책입니다. 객체 간의 상호작용과 책임 분배를 통해 재사용 가능하고 유지보수가 쉬운 코드를 작성하기 위한 설계 템플릿입니다.

📝 개념 정리

  • GOF(Gang of Four): 에리히 감마, 리처드 헬름, 랄프 존슨, 존 블리시디스가 정의한 23개 패턴
  • 패턴의 구성: 이름, 문제, 해법, 결과로 구성
  • 카테고리: 생성(Creational), 구조(Structural), 행동(Behavioral) 패턴
  • 안티패턴: 잘못된 설계 관행들 (싱글톤 남용, God Object 등)

🔑 디자인 패턴의 분류

1. 생성 패턴 (Creational Patterns)

객체 생성과 관련된 패턴들

  • 싱글톤(Singleton): 클래스의 인스턴스를 하나로 제한
  • 팩토리(Factory): 객체 생성 로직을 캡슐화
  • 빌더(Builder): 복잡한 객체를 단계별로 생성
  • 프로토타입(Prototype): 기존 객체를 복제하여 새 객체 생성

2. 구조 패턴 (Structural Patterns)

클래스나 객체의 구성과 관련된 패턴들

  • 어댑터(Adapter): 호환되지 않는 인터페이스를 연결
  • 데코레이터(Decorator): 객체에 새로운 기능을 동적으로 추가
  • 퍼사드(Facade): 복잡한 시스템에 단순한 인터페이스 제공
  • 컴포지트(Composite): 트리 구조로 객체들을 구성

3. 행동 패턴 (Behavioral Patterns)

객체 간의 상호작용과 책임 분배와 관련된 패턴들

  • 옵저버(Observer): 객체 상태 변화를 관련 객체들에게 알림
  • 전략(Strategy): 알고리즘을 교체 가능하게 만듦
  • 커맨드(Command): 요청을 객체로 캡슐화
  • 상태(State): 객체의 상태에 따라 행동 변경

🎮 게임 개발에서 자주 사용되는 패턴

1. 싱글톤 패턴 (Singleton Pattern)

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
// 게임 매니저 - 전역 접근이 필요한 시스템
public class GameManager
{
    private static GameManager instance;
    private static readonly object lockObject = new object();

    public static GameManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                        instance = new GameManager();
                }
            }
            return instance;
        }
    }

    private GameManager() { }

    public GameState CurrentState { get; private set; }
    public void ChangeState(GameState newState) { /* 구현 */ }
}

// 사용
GameManager.Instance.ChangeState(GameState.Playing);

2. 옵저버 패턴 (Observer Pattern)

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
// 게임 이벤트 시스템
public interface IGameEventObserver
{
    void OnPlayerLevelUp(int newLevel);
    void OnEnemyDefeated(Enemy enemy);
    void OnItemCollected(Item item);
}

public class GameEvents
{
    private List<IGameEventObserver> observers = new List<IGameEventObserver>();

    public void Subscribe(IGameEventObserver observer)
    {
        observers.Add(observer);
    }

    public void NotifyPlayerLevelUp(int newLevel)
    {
        foreach (var observer in observers)
        {
            observer.OnPlayerLevelUp(newLevel);
        }
    }
}

// UI 시스템이 게임 이벤트를 구독
public class UIManager : IGameEventObserver
{
    public void OnPlayerLevelUp(int newLevel)
    {
        ShowLevelUpNotification(newLevel);
    }

    public void OnEnemyDefeated(Enemy enemy)
    {
        UpdateScore(enemy.Points);
    }

    public void OnItemCollected(Item item)
    {
        UpdateInventoryDisplay();
    }
}

3. 전략 패턴 (Strategy Pattern)

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
// AI 행동 전략
public interface IAIBehavior
{
    void Execute(Enemy enemy, Player target);
}

public class AggressiveAI : IAIBehavior
{
    public void Execute(Enemy enemy, Player target)
    {
        // 적극적으로 플레이어 추적 및 공격
        enemy.MoveTowards(target.Position);
        if (enemy.IsInRange(target))
            enemy.Attack(target);
    }
}

public class DefensiveAI : IAIBehavior
{
    public void Execute(Enemy enemy, Player target)
    {
        // 체력이 낮으면 도망, 높으면 천천히 접근
        if (enemy.Health < enemy.MaxHealth * 0.3f)
            enemy.Retreat();
        else
            enemy.PatrolArea();
    }
}

public class Enemy
{
    private IAIBehavior behavior;

    public void SetBehavior(IAIBehavior newBehavior)
    {
        behavior = newBehavior;
    }

    public void Update(Player target)
    {
        behavior?.Execute(this, target);
    }
}

💡 게임별 패턴 활용 예시

RPG 게임에서의 패턴 활용

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
// 팩토리 패턴 - 아이템 생성
public class ItemFactory
{
    public static Item CreateItem(ItemType type, int level)
    {
        return type switch
        {
            ItemType.Weapon => new Weapon(level),
            ItemType.Armor => new Armor(level),
            ItemType.Potion => new Potion(level),
            _ => throw new ArgumentException("Unknown item type")
        };
    }
}

// 커맨드 패턴 - 스킬 시스템
public interface ISkillCommand
{
    void Execute(Character caster, ITarget target);
    bool CanExecute(Character caster);
    void Undo(); // 일부 스킬은 되돌리기 가능
}

public class FireballCommand : ISkillCommand
{
    public void Execute(Character caster, ITarget target)
    {
        var damage = caster.Intelligence * 2;
        target.TakeDamage(damage);
        caster.ConsumeMana(50);
    }

    public bool CanExecute(Character caster)
    {
        return caster.Mana >= 50;
    }

    public void Undo() { /* 힐링 스킬의 경우 되돌리기 */ }
}

액션 게임에서의 패턴 활용

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
// 상태 패턴 - 플레이어 상태 관리
public abstract class PlayerState
{
    public abstract void HandleInput(Player player, InputData input);
    public abstract void Update(Player player, float deltaTime);
    public abstract void OnEnter(Player player);
    public abstract void OnExit(Player player);
}

public class IdleState : PlayerState
{
    public override void HandleInput(Player player, InputData input)
    {
        if (input.IsMoving)
            player.ChangeState(new RunningState());
        else if (input.IsJumping)
            player.ChangeState(new JumpingState());
    }

    public override void Update(Player player, float deltaTime)
    {
        // 대기 애니메이션 재생
    }

    public override void OnEnter(Player player)
    {
        player.PlayAnimation("idle");
    }

    public override void OnExit(Player player) { }
}

public class Player
{
    private PlayerState currentState;

    public void ChangeState(PlayerState newState)
    {
        currentState?.OnExit(this);
        currentState = newState;
        currentState.OnEnter(this);
    }

    public void Update(float deltaTime)
    {
        currentState.Update(this, deltaTime);
    }
}

🎯 실무에서 자주 묻는 면접 질문

Q: 싱글톤 패턴의 장단점은? A:

  • 장점: 전역 접근, 메모리 절약, 인스턴스 제어
  • 단점: 테스트 어려움, 결합도 증가, 멀티스레딩 이슈
  • 게임에서: GameManager, AudioManager 등에 사용하지만 DI로 대체 권장

Q: 옵저버 패턴과 이벤트 시스템의 차이는? A: 옵저버 패턴은 직접적인 객체 참조를 통한 알림이고, 이벤트 시스템은 중간 매개체(EventBus)를 통한 느슨한 결합 방식입니다.

Q: 어떤 패턴을 가장 많이 사용해봤나요? A: “전략 패턴을 AI 시스템에서, 옵저버 패턴을 UI 업데이트에서, 팩토리 패턴을 오브젝트 풀링과 함께 사용했습니다. 특히 전략 패턴은 런타임에 AI 행동을 바꿀 수 있어 유용했습니다.”


🔗 관련 개념


이 문서는 디자인 패턴의 기본 개념과 게임 개발에서의 활용을 다룹니다. 각 패턴에 대한 상세한 구현과 더 많은 예제는 추후 별도 포스트에서 다룰 예정입니다.

This post is licensed under CC BY 4.0 by the author.