◎ 정의
옵서버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다. - 출처 : 위키백과 |
- 즉 관찰자 패턴은 여러 객체들 간에 일대다의 의존성을 갖고서 한 객체의 상태에 변경사항이 생기면 그 객체에 의존적인 다른 나머지 객체들이 변경사항을 통보받아서 지시한 동작이 알아서 실행되도록 할 때 사용된다.
◎ 활용
- 게임 내 업적 시스템이나 캐릭터의 상태를 나타내는 UI 등 정보를 받아야 하는 주체가 계속해서 해당 데이터를 확인하는 것이 아니라 특정 이벤트가 발생했을 때만 처리하면 되는 모든 부분에서 유용하게 쓰인다.
◎ 장점 / 단점
○ 장점
- 객체 간의 결합도가 느슨하면서 일관성 유지가 가능하다.
- 객체 간 서로 상호작용하므로 변경된 정보를 갱신하는데 용이하다.
○ 단점
- 등록한 관찰자를 삭제할 때 removeObserver()와 같은 일련의 처리를 하지 않으면 불필요한 퍼포먼스 낭비가 일어날 가능성이 있다.
- 각각의 관찰자에서 처리되는 작업이 모호한 경우가 생길 수 있다. 가령 동시에 A와 B라는 이벤트가 발생했을 때 A와 B가 서로의 상태에 따라 각기 다른 처리를 하도록 참조된 경우 해당 이벤트의 처리 순서에 따라 결과가 달라지는 상황이 생길 수 있다.
- 프로그램에서 코드가 서로 어떻게 상호작용하는지를 알기가 어렵기 때문에 런타임 때에 디버그를 걸어서 확인해야 하는 상황이 생길 수 있다.
◎ 구현 코드
○ 기본 구현
< Observer.cs >
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IObserver
{
void Notify();
void AddObserver(Observer observer);
void RemoveObserver(Observer observer);
}
public class Observer : MonoBehaviour
{
public virtual void OnNotify() { }
}
- IObserver의 인터페이스를 상속받아 구현하면 이벤트의 주체(Subject) 역할을 하게 되며 Observer 클래스를 상속할 경우 관찰자의 역할을 하게 된다.
< Box.cs >
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Box : Observer /*관찰자등록*/
{
public GameObject box_object; // 대상 객체와 연결필요
public Subject subject; // 관찰자 주체 연결
private void Start()
{
if (subject != null)
{
subject.AddObserver(this);
}
}
public override void OnNotify()
{
if (box_object != null)
{
RandomMove();
Debug.Log("이벤트 수신!");
}
void RandomMove()
{
box_object.transform.position = new Vector3(Random.Range(0, 10f), Random.Range(0, 10f), Random.Range(0, 10f));
}
}
}
- Box 클래스는 자기 자신을 관찰자로 등록하고 이벤트가 수신되면 box_object의 위치를 랜덤 하게 이동시킨다.
< Subject.cs >
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Subject : MonoBehaviour, IObserver
{
List<Observer> observer_list = new List<Observer>(); // 관찰자를 담는 리스트
float time;
private void Update()
{
time += Time.deltaTime;
if (time > 3.0f)
{
Notify();
time = 0;
Debug.Log("이벤트 발신!");
}
}
// 전체에 이벤트 전송
public void Notify()
{
for(int i = 0; i < observer_list.Count; ++i)
{
observer_list[i].OnNotify();
}
}
// 관찰자 추가
public void AddObserver(Observer observer)
{
observer_list.Add(observer);
}
// 관찰자 제거
public void RemoveObserver(Observer observer)
{
observer_list.Remove(observer);
}
}
- 관찰자 주체로써 구현해야 할 함수를 정의하고 3초마다 모든 관찰자에게 메시지를 보낸다.
○ 유니티 엔진 C#의 Delegate 사용
- 관찰자 패턴의 기본적인 구현은 위와 같지만 유니티 엔진의 C#에서는 Delegate를 이용하면 같은 기능을 더욱 쉽게 구현이 가능하다.
< Subject.cs >
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Subject : MonoBehaviour
{
public delegate void event_handler();
public event_handler evnethandler;
float time;
private void Update()
{
time += Time.deltaTime;
if (time > 3.0f)
{
time = 0;
evnethandler();
Debug.Log("이벤트 발신!");
}
}
}
- 이벤트를 담을 델리게이트를 선언한다.
< Box.cs >
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Box : MonoBehaviour
{
public GameObject box_object; // 대상 객체와 연결필요
public Subject subject; // 관찰자 주체 연결
private void Start()
{
if (subject != null)
{
subject.evnethandler += new Subject.event_handler(OnNotify);
}
}
public void OnNotify()
{
if (box_object != null)
{
RandomMove();
Debug.Log("이벤트 수신!");
}
void RandomMove()
{
box_object.transform.position = new Vector3(Random.Range(0, 10f), Random.Range(0, 10f), Random.Range(0, 10f));
}
}
}
- Subject에 있는 델리게이트에 원하는 동작을 추가한다.
'Programming > 디자인패턴' 카테고리의 다른 글
[디자인패턴] 경량 패턴, 플라이웨이트 패턴 (Flyweight Pattern) (0) | 2020.07.01 |
---|---|
[디자인패턴] 명령 패턴, 커맨드 패턴 (Command Pattern) (0) | 2020.06.30 |