← Назад к вопросам

Для чего нужен Observer?

2.2 Middle🔥 112 комментариев
#C# и ООП#Паттерны проектирования

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Роль паттерна Observer в разработке на Unity

Observer (Наблюдатель) — это поведенческий паттерн проектирования, который создаёт механизм подписки для уведомления множества объектов об изменениях состояния другого объекта. В контексте Unity и геймдева он является одним из краеугольных камней архитектуры, особенно для создания гибких, слабосвязанных и масштабируемых систем.

Основные цели применения Observer в Unity:

  • Реализация событийной модели (Event-driven architecture): Unity изначально построен на событиях (Start, Update, OnCollisionEnter). Паттерн Observer позволяет расширить эту модель, создавая собственные, предметно-ориентированные события (например, OnPlayerHealthChanged, OnEnemyKilled, OnInventoryUpdated).
  • Ослабление связанности (Loose Coupling): Объекты-издатели (Subject) не знают конкретных деталей о подписчиках (Observers). Они просто уведомляют всех, кто проявил интерес. Это позволяет легко добавлять новые реакции на события, не модифицируя исходный код издателя.
  • Управление сложными взаимодействиями: В игре десятки систем (UI, звук, геймплей, анимации) должны реагировать на изменения. Observer организует это взаимодействие чисто и предсказуемо, избегая "спагетти-кода" с прямыми вызовами методов между скриптами.

Практическая реализация в Unity

В Unity паттерн Observer чаще всего реализуется через механизм C# событий (event) и делегатов (Action, UnityAction), либо через более продвинутые фреймворки вроде UnityEvent в инспекторе.

Пример: Система здоровья игрока с уведомлением UI и системы достижений.

// Издатель (Subject) - компонент здоровья
public class PlayerHealth : MonoBehaviour
{
    // 1. Объявляем событие (канал уведомлений)
    public event Action<int, int> OnHealthChanged; // Текущее, Максимальное
    public event Action OnPlayerDeath;

    private int _currentHealth;
    private int _maxHealth = 100;

    void Start()
    {
        _currentHealth = _maxHealth;
    }

    public void TakeDamage(int damage)
    {
        _currentHealth -= damage;
        _currentHealth = Mathf.Clamp(_currentHealth, 0, _maxHealth);

        // 2. Генерируем событие при изменении состояния
        OnHealthChanged?.Invoke(_currentHealth, _maxHealth);

        if (_currentHealth <= 0)
        {
            OnPlayerDeath?.Invoke();
        }
    }
}
// Подписчик (Observer) 1 - UI контроллер
public class HealthUI : MonoBehaviour
{
    [SerializeField] private Slider _healthSlider;
    [SerializeField] private PlayerHealth _playerHealth;

    void OnEnable()
    {
        // 3. Подписываемся на событие
        _playerHealth.OnHealthChanged += UpdateHealthBar;
    }

    void OnDisable()
    {
        // 4. Важно: отписываемся при уничтожении объекта
        _playerHealth.OnHealthChanged -= UpdateHealthBar;
    }

    // 5. Реакция на событие
    private void UpdateHealthBar(int current, int max)
    {
        _healthSlider.value = (float)current / max;
    }
}
// Подписчик (Observer) 2 - Менеджер достижений
public class AchievementManager : MonoBehaviour
{
    [SerializeField] private PlayerHealth _playerHealth;

    void OnEnable()
    {
        _playerHealth.OnPlayerDeath += HandlePlayerDeath;
    }

    void OnDisable()
    {
        _playerHealth.OnPlayerDeath -= HandlePlayerDeath;
    }

    private void HandlePlayerDeath()
    {
        // Разблокируем достижение "Первая смерть"
        UnlockAchievement("FIRST_BLOOD");
    }
}

Ключевые преимущества в геймдеве

  • Масштабируемость: Добавить новую систему, которая реагирует на смерть игрока (например, воспроизведение звука или выпадение лута), теперь требует лишь создания нового класса-подписчика. Код PlayerHealth остаётся неизменным.
  • Тестируемость: Системы можно тестировать изолированно. Для проверки UI здоровья достаточно сгенерировать событие с тестовыми данными.
  • Чистота кода: Логика размазана по системам, но связи между ними централизованы и явно объявлены через события. Это повышает читаемость и поддерживаемость проекта.
  • Интеграция с Unity Inspector: Использование UnityEvent позволяет назначать подписчиков визуально, через перетаскивание объектов, что удобно для дизайнеров и не-программистов.

Заключение: В Unity паттерн Observer не просто "нужен" — он является фундаментальным подходом для организации коммуникации между компонентами игры. Он превращает монолитные, сильно связанные скрипты в модульную сеть независимых систем, которые общаются через чётко определённые событийные контракты. Это критически важно для разработки средних и крупных проектов, где требования постоянно меняются, а кодовая база растёт.

Для чего нужен Observer? | PrepBro