文章目录
-
-
- [🎯 观察者模式(Observer Pattern)深度解析](#🎯 观察者模式(Observer Pattern)深度解析)
-
- 一、模式本质与核心价值
- 二、经典UML结构
- 三、Unity实战代码(玩家血量监控系统)
-
- [1. 定义观察者接口与主题基类](#1. 定义观察者接口与主题基类)
- [2. 实现具体主题(玩家血量)](#2. 实现具体主题(玩家血量))
- [3. 实现具体观察者](#3. 实现具体观察者)
- [4. 客户端使用](#4. 客户端使用)
- 四、模式进阶技巧
-
- [1. 事件总线系统](#1. 事件总线系统)
- [2. 条件过滤器](#2. 条件过滤器)
- [3. 异步事件处理](#3. 异步事件处理)
- 五、游戏开发典型应用场景
- 六、性能优化策略
- 七、模式对比与选择
- 八、最佳实践原则
- 九、常见问题解决方案
-
🎯 观察者模式(Observer Pattern)深度解析
------以Unity实现动态事件通知 与跨系统响应为核心案例
一、模式本质与核心价值
核心目标 :
✅ 建立对象间的一对多依赖 ,实现状态变化自动通知
✅ 解耦主题与观察者 ,提升系统扩展性与维护性
✅ 支持动态订阅机制,灵活管理观察关系
关键术语:
- Subject(主题):状态变化的对象(如玩家血量)
- Observer(观察者):监听状态变化的对象(如UI、成就系统)
- Notification(通知):主题向观察者传递的信息
数学表达 :
设主题S有观察者集合O={o₁, o₂, ..., oₙ},当S变化时:
S.Update() → ∀o ∈ O, o.OnNotify()
二、经典UML结构
<<interface>> ISubject +RegisterObserver(IObserver) +RemoveObserver(IObserver) +NotifyObservers() <<interface>> IObserver +OnNotify() PlayerHealth -observers: List<IObserver> +TakeDamage() +Heal() HealthUI +OnNotify() AchievementSystem +OnNotify()
三、Unity实战代码(玩家血量监控系统)
1. 定义观察者接口与主题基类
csharp
public interface IObserver {
void OnNotify(HealthData data);
}
public interface ISubject {
void RegisterObserver(IObserver observer);
void RemoveObserver(IObserver observer);
void NotifyObservers();
}
[System.Serializable]
public struct HealthData {
public float CurrentHealth;
public float MaxHealth;
public float DamageAmount;
}
2. 实现具体主题(玩家血量)
csharp
public class PlayerHealth : MonoBehaviour, ISubject {
[SerializeField] private float maxHealth = 100f;
private float _currentHealth;
private List<IObserver> _observers = new();
void Start() {
_currentHealth = maxHealth;
}
public void TakeDamage(float damage) {
_currentHealth = Mathf.Max(_currentHealth - damage, 0);
NotifyObservers(new HealthData {
CurrentHealth = _currentHealth,
MaxHealth = maxHealth,
DamageAmount = damage
});
if(_currentHealth <= 0) Die();
}
public void RegisterObserver(IObserver observer) => _observers.Add(observer);
public void RemoveObserver(IObserver observer) => _observers.Remove(observer);
private void NotifyObservers(HealthData data) {
foreach(var observer in _observers) {
observer.OnNotify(data);
}
}
}
3. 实现具体观察者
csharp
// 血条UI
public class HealthBarUI : MonoBehaviour, IObserver {
[SerializeField] private Image healthFill;
public void OnNotify(HealthData data) {
healthFill.fillAmount = data.CurrentHealth / data.MaxHealth;
Debug.Log($"血条更新:{healthFill.fillAmount:P0}");
}
}
// 成就系统
public class AchievementTracker : MonoBehaviour, IObserver {
private int _consecutiveHits;
public void OnNotify(HealthData data) {
if(data.DamageAmount > 0) {
_consecutiveHits++;
if(_consecutiveHits >= 3) {
Debug.Log("解锁成就:连续受伤三次!");
}
} else {
_consecutiveHits = 0;
}
}
}
4. 客户端使用
csharp
public class GameManager : MonoBehaviour {
[SerializeField] private PlayerHealth playerHealth;
[SerializeField] private HealthBarUI healthUI;
[SerializeField] private AchievementTracker achievementTracker;
void Start() {
playerHealth.RegisterObserver(healthUI);
playerHealth.RegisterObserver(achievementTracker);
}
void Update() {
if(Input.GetKeyDown(KeyCode.Space)) {
playerHealth.TakeDamage(10);
}
}
}
四、模式进阶技巧
1. 事件总线系统
csharp
public static class EventBus {
private static Dictionary<Type, List<Action<object>>> _handlers = new();
public static void Subscribe<T>(Action<T> handler) {
Type type = typeof(T);
if(!_handlers.ContainsKey(type)) _handlers[type] = new List<Action<object>>();
_handlers[type].Add(obj => handler((T)obj));
}
public static void Publish<T>(T eventData) {
Type type = typeof(T);
if(_handlers.TryGetValue(type, out var handlers)) {
foreach(var h in handlers) h(eventData);
}
}
}
// 使用示例
EventBus.Subscribe<HealthData>(data => {
// 处理健康数据...
});
2. 条件过滤器
csharp
public class FilteredObserver : IObserver {
private Predicate<HealthData> _filter;
private Action<HealthData> _action;
public FilteredObserver(Predicate<HealthData> filter, Action<HealthData> action) {
_filter = filter;
_action = action;
}
public void OnNotify(HealthData data) {
if(_filter(data)) _action(data);
}
}
// 使用示例:仅在血量低于30%时触发
var lowHealthObserver = new FilteredObserver(
data => data.CurrentHealth/data.MaxHealth < 0.3f,
data => ShowWarning()
);
3. 异步事件处理
csharp
public class AsyncEventProcessor : MonoBehaviour {
private Queue<HealthData> _eventQueue = new();
public void QueueEvent(HealthData data) {
_eventQueue.Enqueue(data);
}
void Update() {
while(_eventQueue.Count > 0) {
StartCoroutine(ProcessEvent(_eventQueue.Dequeue()));
}
}
private IEnumerator ProcessEvent(HealthData data) {
// 异步处理逻辑
yield return null;
}
}
五、游戏开发典型应用场景
-
成就系统触发
csharppublic class AchievementSystem : IObserver { public void OnNotify(EnemyDeathData data) { if(data.EnemyType == EnemyType.Boss) { UnlockAchievement("BOSS_SLAYER"); } } }
-
全局事件通知
csharppublic class GlobalEvent : ISubject { private static GlobalEvent _instance; public static GlobalEvent Instance => _instance ??= new GlobalEvent(); // 实现观察者注册/通知逻辑... }
-
技能冷却系统
csharppublic class SkillManager : IObserver { public void OnNotify(SkillEventData data) { if(data.EventType == SkillEventType.Used) { StartCooldown(data.SkillID); } } }
-
环境互动反馈
csharppublic class EnvironmentFX : IObserver { public void OnNotify(PlayerMoveData data) { if(data.IsInWater) PlayWaterRippleFX(data.Position); } }
六、性能优化策略
策略 | 实现方式 | 适用场景 |
---|---|---|
事件过滤 | 前置条件检查 | 高频事件 |
批处理 | 合并多个事件 | 物理系统更新 |
对象池 | 重用事件对象 | 频繁事件触发 |
分层处理 | 优先级队列 | 关键事件优先 |
七、模式对比与选择
维度 | 观察者模式 | 发布-订阅模式 |
---|---|---|
耦合度 | 观察者直接注册到主题 | 通过中间件解耦 |
灵活性 | 需要知道具体主题 | 无需知道发布者 |
性能 | 直接调用更高效 | 中间件可能引入开销 |
典型应用 | 组件间直接通信 | 系统级事件管理 |
八、最佳实践原则
-
避免过度通知 :仅在状态真正变化时触发通知
csharpprivate float _previousHealth; void Update() { if(Mathf.Abs(_currentHealth - _previousHealth) > 0.01f) { NotifyObservers(); _previousHealth = _currentHealth; } }
-
内存管理 :及时取消不再需要的订阅
csharpvoid OnDestroy() { playerHealth.RemoveObserver(this); }
-
线程安全 :在多线程环境使用锁机制
csharpprivate readonly object _lock = new object(); public void RegisterObserver(IObserver observer) { lock(_lock) { _observers.Add(observer); } }
-
事件数据不可变 :
csharppublic readonly struct ImmutableHealthData { public readonly float Current; public ImmutableHealthData(float current) => Current = current; }
九、常见问题解决方案
Q1:如何处理循环通知?
→ 实现事件标记防止递归
csharp
private bool _isNotifying;
public void NotifyObservers() {
if(_isNotifying) return;
_isNotifying = true;
// 通知逻辑...
_isNotifying = false;
}
Q2:如何优化大量观察者的性能?
→ 使用分层观察者
csharp
public class TieredObserverSystem {
private Dictionary<EventPriority, List<IObserver>> _tiers = new();
public void NotifyByPriority() {
foreach(var tier in Enum.GetValues(typeof(EventPriority))) {
foreach(var observer in _tiers[(EventPriority)tier]) {
observer.OnNotify();
}
}
}
}
Q3:如何调试复杂事件流?
→ 实现事件追踪器
csharp
public class EventDebugger : IObserver {
public void OnNotify(object data) {
Debug.Log($"[Event] {DateTime.Now:HH:mm:ss.fff} - {data.GetType().Name}");
// 记录到文件或调试窗口...
}
}
上一篇 【行为型之备忘录模式】游戏开发实战------Unity存档系统与状态管理的终极解决方案
下一篇 【行为型之状态模式】深度剖析------Unity角色行为控制与AI决策的终极解决方案