발행자 수 | 구독자 수 | 대응 방식 |
---|---|---|
1 | 1 | 함수 호출 |
1 | can be N | 옵저버 패턴 |
can be N | can be N | 이벤트 버스 패턴 |
이벤트 기반 설계 시 많이 활용하는 패턴
발행자가 이벤트가 발생했을 때 알려주는 패턴
단, 구독을 하면 나와 상관없는 것도 받아야 할 수 있음.
입문 강의 내용 중,
TopDownController의 OnMoveEvent를 두 구독자들이 각각의 Move 메서드로 구독하고 있었음.
// 발행자
public class TopDownController : MonoBehaviour
{
public event Action<Vector2> OnMoveEvent;
// 생략
}
// 구독자1
public class TopDownMovement : MonoBehaviour
{
public TopDownController controller;
void Start()
{
controller += Move;
}
void Move(Vector2 direction)
{
// ~
}
}
// 구독자2
public class TopDownAnimationController : MonoBehaviour
{
public TopDownController controller;
void Start()
{
controller += Move;
}
void Move(Vector2 direction)
{
// ~
}
}
1개 이상의 발행자와 1개 이상의 구독자의 처리가 가능하다.
이벤트 구현 시 발행자와 구독자 사이에서 직접적인 의존성을 만들지 않고 이벤트 기능을 만들 수 있다.
퀘스트 시스템 등
EventBus에는 이벤트를 등록.
Publisher가 이벤트를 Invoke. 이때 Publisher는 하나일 필요는 없음.
하나 이상의 Publisher
EventBus는 하나 이상의 액션을 관리하기 때문에 대체로 Dictionary를 이용해서 구현
Publisher에게 직접 구독하지 않고 구독을 받아주는 대리자를 만들어서 모든 구독을 받음
Publisher들은 이 대리자를 통해서 이벤트를 발행시킴.
using System;
using System.Collections.Generic;
using UnityEngine;
public enum QuestEvent
{
Hunt,
Explore,
Build
}
public static class QuestEventBus
{
static Dictionary<QuestEvent, Action> questEventMap = new Dictionary<QuestEvent, Action>();
// 등록
public static void Subscribe(QuestEvent questEvent, Action listener)
{
if (questEventMap.TryGetValue(questEvent, out Action questAction))
{
questAction += listener;
questEventMap[questEvent] = listener;
}
else
{
questAction = listener;
questEventMap.Add(questEvent, questAction);
}
}
// 해제
public static void Unsubscribe(QuestEvent questEvent, Action listener)
{
if (questEventMap.TryGetValue(questEvent, out Action questAction))
{
questAction -= listener;
if(questAction == null)
{
questEventMap.Remove(questEvent);
}
else
{
questEventMap[questEvent] = listener;
}
}
}
public static void Publish(QuestEvent questEvent)
{
if (questEventMap.TryGetValue(questEvent, out Action questAction))
{
questAction?.Invoke();
}
}
}
// 사용 예시
public class QuestListener : MonoBehaviour
{
[SerializeField] QuestEvent questType = QuestEvent.Hunt;
void OnEnable()
{
QuestEventBus.Subscribe(questType, Test);
}
void OnDisable()
{
QuestEventBus.Unsubscribe(questType, Test);
}
void Test()
{
Debug.Log("Listener Test called");
}
}
public class QuestPublisher : MonoBehaviour
{
public void OnDead()
{
QuestEventBus.Publish(QuestEvent.Hunt);
}
}
옵저버, 이벤트 버스 패턴이 비슷함.
그래서 발행-구독 패턴 Pub-Sub 패턴이라고 함.
이벤트 버스
명령(행동) 하나하나를 객체로 만든다.
목적에 따라 구현이 달라질 수 있음.