Какие невалидные состояния объекта вы знаете?

«Какие невалидные состояния объекта вы знаете?» — вопрос из категории ООП, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Невалидное состояние объекта возникает, когда его внутренние данные противоречат бизнес-правилам или инвариантам класса, делая объект непригодным для использования. Это ключевая проблема, ведущая к ошибкам времени выполнения.

Распространенные примеры невалидных состояний:

  1. Нарушение обязательности полей (Null в обязательных полях):

    public class User
    {
        // Имя пользователя обязательно, но свойство может быть null
        public string Name { get; set; }
    }
    // Невалидное состояние: user.Name == null
  2. Нарушение допустимых диапазонов значений:

    public class OrderItem
    {
        // Количество не может быть отрицательным или нулевым
        public int Quantity { get; set; }
    }
    // Невалидное состояние: item.Quantity == -5
  3. Нарушение бизнес-инвариантов:

    public class BankAccount
    {
        // Баланс не должен быть отрицательным, если не предусмотрен овердрафт
        public decimal Balance { get; set; }
    }
    // Невалидное состояние: account.Balance == -100.00m
  4. Внутренняя противоречивость данных:

    public class DateRange
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
        // Инвариант: End всегда >= Start
    }
    // Невалидное состояние: range.End < range.Start
  5. Неполная или некорректная инициализация:

    public class Product
    {
        public int Id { get; }
        // Конструктор забыл присвоить значение Id
        public Product(int id) { /* Id = id; // ОШИБКА: пропущено */ }
    }
    // Невалидное состояние: product.Id == 0 (значение по умолчанию)

Стратегии защиты от невалидных состояний:

  • Валидация в конструкторах и сеттерах: Гарантирует, что объект не может быть создан или изменен в невалидном состоянии.
  • Принцип Guard Clauses: Проверка входящих аргументов в начале метода.
    public void SetQuantity(int quantity)
    {
        if (quantity <= 0) throw new ArgumentOutOfRangeException(nameof(quantity));
        _quantity = quantity;
    }
  • Использование неизменяемых (Immutable) объектов: Состояние устанавливается один раз при создании и после этого не меняется, что упрощает валидацию.
  • Введение Value Objects: Замена примитивных типов (int, string, decimal) на специализированные типы, которые сами обеспечивают свою валидность (например, EmailAddress, PositiveInteger, Money).