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

Приведи пример абстракции

1.0 Junior🔥 201 комментариев
#C# и ООП

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

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

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

Пример абстракции в разработке игр на Unity

Абстракция в программировании — это фундаментальный принцип ООП, который заключается в сокрытии сложной внутренней реализации и предоставлении пользователю (другому программисту или системе) только простого и понятного интерфейса для взаимодействия. В контексте Unity-разработки абстракция невероятно важна для создания чистой, поддерживаемой и масштабируемой архитектуры игры, особенно когда над проектом работает команда.

Практический пример: Система управления персонажем (Character Controller)

Представим, что нам нужно создать систему управления персонажем, которая должна работать как для игрока, так и для AI, и поддерживать разные типы передвижения (пешком, на транспорте, полёт). Если вся логика будет размазана по скриптам PlayerInput и EnemyAI, мы быстро получим "спагетти-код". Вместо этого мы создаём абстракцию.

Шаг 1: Определяем абстрактный интерфейс для перемещения

Создадим интерфейс IMovable, который задаёт контракт — "что может делать любой объект, способный к перемещению", скрывая детали "как он это делает".

// Абстракция: Интерфейс, скрывающий детали реализации движения.
public interface IMovable
{
    // Публичный метод, который представляет собой простой контракт.
    void Move(Vector3 direction);
    float CurrentSpeed { get; }
}

Теперь любой класс, который хочет реализовать перемещение, должен выполнить этот контракт. Детали (расчёт физики, анимация, потребление выносливости) скрыты внутри конкретных реализаций.

Шаг 2: Реализуем конкретные классы, скрывая сложность

Создадим конкретную реализацию для управления игроком. Вся сложность (обработка ввода, работа с компонентом Rigidbody) инкапсулирована внутри класса. Для внешнего кода важен только метод Move.

// Конкретная реализация для персонажа игрока.
public class PlayerMovement : MonoBehaviour, IMovable
{
    [SerializeField] private float _walkSpeed = 5f;
    [SerializeField] private Rigidbody _rb;
    private float _currentStamina = 100f;

    // Публичное свойство из интерфейса.
    public float CurrentSpeed => _rb.velocity.magnitude;

    // Публичный метод из интерфейса. ВНУТРИ скрыта вся сложность.
    public void Move(Vector3 direction)
    {
        // 1. Скрытая логика: нормализация и применение скорости.
        Vector3 moveForce = direction.normalized * _walkSpeed;
        
        // 2. Скрытая логика: расчёт выносливости.
        if (direction.magnitude > 0.1f)
        {
            _currentStamina -= Time.deltaTime * 2f;
            moveForce *= Mathf.Clamp(_currentStamina / 100f, 0.5f, 1f);
        }
        
        // 3. Скрытая логика: применение силы через Rigidbody.
        _rb.AddForce(moveForce, ForceMode.VelocityChange);
        
        // 4. Скрытая логика: вызов события для аниматора.
        OnMovementUpdated?.Invoke(CurrentSpeed);
        // Внешний код НЕ ЗНАЕТ об этих деталях!
    }

    // Деталь реализации, скрытая от внешнего мира.
    private void OnMovementUpdated(float speed) { /* ... */ }
}

Шаг 3: Создаём абстракцию более высокого уровня

Теперь мы можем создать менеджер управления (CharacterController), который работает только с абстракцией IMovable. Ему абсолютно всё равно, кем является сущность — игроком, NPC или танком.

// Класс высокого уровня, зависящий от абстракции, а не от деталей.
public class CharacterController : MonoBehaviour
{
    private IMovable _movementSystem;

    public void Initialize(IMovable movementSystem)
    {
        _movementSystem = movementSystem; // Принимаем ЛЮБУЮ реализацию IMovable
    }

    public void UpdateController(Vector3 inputDirection)
    {
        // Контроллер просто вызывает абстрактный метод.
        // Он не знает, как именно будет выполнено движение.
        _movementSystem.Move(inputDirection);

        // Может использовать абстрактное свойство.
        Debug.Log($"Текущая скорость: {_movementSystem.CurrentSpeed}");
    }
}

Шаг 4: Преимущества и расширение системы

Благодаря такой абстракции мы получаем огромные преимущества:

  • Снижение связанности: CharacterController не зависит от PlayerMovement. Он зависит от интерфейса.
  • Упрощение тестирования: Мы можем создать Mock-объект (заглушку) для тестирования контроллера.
  • Легкость расширения: Добавить новый тип перемещения (например, FlyingMovement или VehicleMovement) становится тривиально. Нужно лишь реализовать интерфейс IMovable.
// Новая реализация добавляется без изменения существующего кода контроллера.
public class FlyingMovement : MonoBehaviour, IMovable
{
    public float CurrentSpeed => /* своя логика */;
    public void Move(Vector3 direction)
    {
        // Совершенно иная реализация: игнорирование гравитации,
        // использование силы подъёма, трата маны и т.д.
        // Для CharacterController это не имеет значения!
    }
}

Итог

В этом примере абстракция представлена интерфейсом IMovable, который служит контрактом, скрывающим:

  1. Физическую модель (Rigidbody, CharacterController).
  2. Ресурсы (выносливость, мана, топливо).
  3. Вспомогательные системы (анимация, звуки).

Потребитель (в нашем случае CharacterController) взаимодействует с простым, понятным методом Move(), не вникая в сложности его реализации. Это позволяет строить гибкую архитектуру, где конкретные механики можно заменять и комбинировать, минимально затрагивая остальной код игры — ключевой навык для профессионального Unity Developer.

Приведи пример абстракции | PrepBro