반응형
옵저버 패턴
객체들 간의 일대다 의존 관계를 정의하여,
한 객체의 상태가 변경되면 그것을 구독하는 모든 객체들이 자동으로 알림을 받고 업데이트되는 패턴
기본 구현 방법
1. 이벤트를 정의할 인터페이스
public interface ISubject
{
void AddObserver(IObserver observer);
void RemoveObserver(IObserver observer);
void NotifyObservers();
}
public interface IObserver
{
void OnNotify(string eventType, object data);
}
2. 게임 이벤트를 관리하는 Subject 클래스
public class PlayerSubject : MonoBehaviour, ISubject
{
private List<IObserver> observers = new List<IObserver>();
private int health = 100;
private int score = 0;
public void AddObserver(IObserver observer)
{
observers.Add(observer);
}
public void RemoveObserver(IObserver observer)
{
observers.Remove(observer);
}
public void NotifyObservers()
{
foreach (var observer in observers)
{
observer.OnNotify("PlayerUpdate", new PlayerData { health = health, score = score });
}
}
// 플레이어 상태 변경 메서드
public void TakeDamage(int damage)
{
health -= damage;
NotifyObservers();
}
public void AddScore(int points)
{
score += points;
NotifyObservers();
}
}
3. 데이터 구조체
public struct PlayerData
{
public int health;
public int score;
}
4. 옵저버 구현 클래스들
public class UIObserver : MonoBehaviour, IObserver
{
public Text healthText;
public Text scoreText;
public void OnNotify(string eventType, object data)
{
if (eventType == "PlayerUpdate")
{
PlayerData playerData = (PlayerData)data;
UpdateUI(playerData);
}
}
private void UpdateUI(PlayerData data)
{
healthText.text = $"Health: {data.health}";
scoreText.text = $"Score: {data.score}";
}
}
public class AchievementObserver : MonoBehaviour, IObserver
{
public void OnNotify(string eventType, object data)
{
if (eventType == "PlayerUpdate")
{
PlayerData playerData = (PlayerData)data;
CheckAchievements(playerData);
}
}
private void CheckAchievements(PlayerData data)
{
if (data.score >= 1000)
{
UnlockAchievement("Score Master");
}
if (data.health <= 10)
{
UnlockAchievement("Close Call");
}
}
private void UnlockAchievement(string achievementName)
{
Debug.Log($"Achievement Unlocked: {achievementName}");
}
}
public class SoundObserver : MonoBehaviour, IObserver
{
private int lastHealth = 100;
public void OnNotify(string eventType, object data)
{
if (eventType == "PlayerUpdate")
{
PlayerData playerData = (PlayerData)data;
CheckHealthForSound(playerData.health);
}
}
private void CheckHealthForSound(int newHealth)
{
if (newHealth < lastHealth)
{
PlayDamageSound();
}
lastHealth = newHealth;
}
private void PlayDamageSound()
{
// 사운드 재생 로직
Debug.Log("Playing damage sound");
}
}
사용예시
// 게임 매니저나 초기화 스크립트에서
void Start()
{
PlayerSubject player = FindObjectOfType<PlayerSubject>();
// 옵저버들을 등록
player.AddObserver(FindObjectOfType<UIObserver>());
player.AddObserver(FindObjectOfType<AchievementObserver>());
player.AddObserver(FindObjectOfType<SoundObserver>());
}
// 플레이어 상태 변경 시
player.TakeDamage(10);
player.AddScore(100);
핵심 개념
- Subject(관찰 대상)와 Observer(관찰자) 간의 일대다 관계 구현
- Subject의 상태 변화를 Observer들에게 자동으로 알림
- Observer들은 Subject에 의존하지 않는 느슨한 결합 유지
- 등록/해제를 통한 동적인 Observer 관리
사용 목적
- 객체 간 결합도 감소
- 시스템 확장성 향상
- 코드 재사용성 증가
- 상태 변화에 따른 자동 업데이트 구현
주요 사용 사례
- 플레이어 상태 변화 감지 (체력, 점수 등)
- UI 업데이트
- 업적 시스템
- 사운드 시스템
- 게임 진행 상태 관리
사용 사례 예시
기본 인터페이스
public interface IObserver
{
void OnNotify(string eventType, object data);
}
플레이어 상태 감시
public class PlayerHealth : MonoBehaviour
{
private List<IObserver> observers = new List<IObserver>();
private int health = 100;
public void TakeDamage(int damage)
{
health -= damage;
NotifyObservers("HealthChanged", health);
}
private void NotifyObservers(string eventType, object data)
{
foreach (var observer in observers)
{
observer.OnNotify(eventType, data);
}
}
}
UI 업데이트 옵저버
public class HealthUI : MonoBehaviour, IObserver
{
private Text healthText;
public void OnNotify(string eventType, object data)
{
if (eventType == "HealthChanged")
{
int health = (int)data;
healthText.text = $"Health: {health}";
}
}
}
업적 시스템 옵저버
public class AchievementSystem : MonoBehaviour, IObserver
{
public void OnNotify(string eventType, object data)
{
if (eventType == "HealthChanged")
{
int health = (int)data;
if (health <= 10)
{
UnlockAchievement("Survivor");
}
}
}
}
결론
1. 장점
- 시스템 간 결합도 감소로 유지보수 용이
- 새로운 Observer 추가가 쉬움
- 코드의 재사용성 향상
- 변경사항 자동 전파 가능
2. 주의사항
- Observer 등록/해제 관리 필요
- 너무 많은 Observer는 성능에 영향
- 순환 참조 방지 필요
- 적절한 사용 범위 설정 중요
옵저버 패턴은 복잡한 시스템에서 객체 간의 상호작용을 체계적으로
관리하고 싶을 때 매우 유용한 디자인 패턴인 것 같다
반응형
'유니티 > 디자인패턴' 카테고리의 다른 글
[Unity] 유니티 C#) Strategy 패턴 (1) | 2024.12.09 |
---|---|
[Unity] 유니티 C#) Visitor 패턴 (0) | 2024.12.06 |
[Unity] 유니티 C#) ObjectPool 패턴 (0) | 2024.12.04 |
[Unity] 유니티 C#) Singleton 패턴 (0) | 2024.12.03 |
[Unity] 유니티 C#) EventBus 패턴 (0) | 2024.12.03 |