지난 시간에 이어 오늘은 옵저버 패턴에 대해 알아보고자 합니다.
2024.10.31 - [디자인 패턴] - 디자인 패턴에 대해 알아보자#4 - 팩토리 패턴 (Factory Pattern) (feat : 팩토리 메소드, 추상 팩토리 패턴)
옵저버 패턴이란?
관찰자가 관찰 대상이 되는 객체의 상태 변화에 따라 대응하는 패턴입니다.
옵저버라는 뜻 그대로 아주 직관적이죠?
예시
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
// 옵저버 인터페이스
public interface IHealthObserver
{
void OnHealthChanged(float currentHealth, float maxHealth);
}
// 플레이어 체력 관리 클래스 (Subject)
public class PlayerHealth : MonoBehaviour
{
[SerializeField] private float maxHealth = 100f;
private float currentHealth;
private List<IHealthObserver> observers = new List<IHealthObserver>();
private void Start()
{
currentHealth = maxHealth;
}
// 옵저버 등록
public void AddObserver(IHealthObserver observer)
{
observers.Add(observer);
}
// 옵저버 제거
public void RemoveObserver(IHealthObserver observer)
{
observers.Remove(observer);
}
// 모든 옵저버에게 통지
private void NotifyObservers()
{
foreach (var observer in observers)
{
observer.OnHealthChanged(currentHealth, maxHealth);
}
}
// 데미지 처리
public void TakeDamage(float damage)
{
currentHealth = Mathf.Max(0f, currentHealth - damage);
NotifyObservers();
if (currentHealth <= 0f)
{
Die();
}
}
// 체력 회복
public void Heal(float amount)
{
currentHealth = Mathf.Min(maxHealth, currentHealth + amount);
NotifyObservers();
}
private void Die()
{
Debug.Log("Player died!");
// 사망 처리 로직
}
}
// UI 업데이트를 처리하는 옵저버
public class HealthUIHandler : MonoBehaviour, IHealthObserver
{
[SerializeField] private Slider healthSlider;
[SerializeField] private Text healthText;
private void Start()
{
// PlayerHealth 컴포넌트 찾아서 옵저버로 등록
var playerHealth = FindObjectOfType<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.AddObserver(this);
}
}
public void OnHealthChanged(float currentHealth, float maxHealth)
{
// UI 업데이트
if (healthSlider != null)
{
healthSlider.value = currentHealth / maxHealth;
}
if (healthText != null)
{
healthText.text = $"Health: {currentHealth:F0}/{maxHealth:F0}";
}
}
}
// 게임 이벤트를 처리하는 옵저버
public class GameEventHandler : MonoBehaviour, IHealthObserver
{
[SerializeField] private float lowHealthThreshold = 30f;
private bool isLowHealthEffectActive = false;
private void Start()
{
var playerHealth = FindObjectOfType<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.AddObserver(this);
}
}
public void OnHealthChanged(float currentHealth, float maxHealth)
{
// 체력이 낮을 때 효과 처리
float healthPercentage = (currentHealth / maxHealth) * 100f;
if (healthPercentage <= lowHealthThreshold && !isLowHealthEffectActive)
{
ActivateLowHealthEffect();
}
else if (healthPercentage > lowHealthThreshold && isLowHealthEffectActive)
{
DeactivateLowHealthEffect();
}
}
private void ActivateLowHealthEffect()
{
isLowHealthEffectActive = true;
Debug.Log("Low health effect activated!");
// 낮은 체력 효과 처리 (화면 효과, 사운드 등)
}
private void DeactivateLowHealthEffect()
{
isLowHealthEffectActive = false;
Debug.Log("Low health effect deactivated!");
// 효과 제거
}
}
// 사용 예시
public class GameManager : MonoBehaviour
{
private PlayerHealth playerHealth;
private void Start()
{
playerHealth = FindObjectOfType<PlayerHealth>();
}
// 테스트용 메서드
public void TestDamage()
{
if (playerHealth != null)
{
playerHealth.TakeDamage(20f);
}
}
public void TestHeal()
{
if (playerHealth != null)
{
playerHealth.Heal(10f);
}
}
}
정리
장점 :
느슨한 결합으로 인한 높은 유지보수성
재사용성과 확장성이 우수
단일 책임 원칙 준수
단점 :
Observer 실행 순서 예측 불가능
이벤트 등록/해제 관리 소홀 시 메모리 누수 발생 위험
많은 Observer 등록 시 성능 저하 가능성
순환 참조 발생 가능성에 대한 주의 필요
마무리
오늘은 간단하게 옵저버 패턴에 대해 정리해 봤습니다.
자료를 찾다 보니 유니티에서 자주 사용되는 패턴이었더군요.
Unity Action도 그렇고, Event 방식이
옵저버 패턴이라고 합니다.
또 과제하면서 분석했던 Interaction과
Inventory도 옵저버 패턴이었습니다.
정확한 명칭을 과제가 끝나고 이제 알았네요;;
어쩐지 분석해 보라고 하더라니.
참고한 자료
https://youtu.be/Sq6LAJ9tqBY?si=TYSycmWx8pzfse8H
https://www.youtube.com/watch?v=CSNaL8Iog1Y
'디자인 패턴' 카테고리의 다른 글
디자인 패턴 - 커맨드 패턴(Command pattern)에 대해 알아보자 (1) | 2024.11.22 |
---|---|
디자인 패턴 - MVC(Model-View-Controller)패턴에 대해 알아보자(+ MVP, MVVM) (0) | 2024.11.07 |
디자인 패턴에 대해 알아보자#4 - 팩토리 패턴 (Factory Pattern) (feat : 팩토리 메소드, 추상 팩토리 패턴) (0) | 2024.10.31 |
디자인 패턴에 대해 알아보자#3 - 전략 패턴 (Strategy) (0) | 2024.10.29 |
디자인 패턴에 대해 알아보자#2 싱글톤 패턴(feat. 유니티) (1) | 2024.10.08 |