Ответ
Невалидное состояние объекта возникает, когда его внутренние данные противоречат бизнес-правилам или инвариантам класса, делая объект непригодным для использования. Это ключевая проблема, ведущая к ошибкам времени выполнения.
Распространенные примеры невалидных состояний:
-
Нарушение обязательности полей (Null в обязательных полях):
public class User { // Имя пользователя обязательно, но свойство может быть null public string Name { get; set; } } // Невалидное состояние: user.Name == null -
Нарушение допустимых диапазонов значений:
public class OrderItem { // Количество не может быть отрицательным или нулевым public int Quantity { get; set; } } // Невалидное состояние: item.Quantity == -5 -
Нарушение бизнес-инвариантов:
public class BankAccount { // Баланс не должен быть отрицательным, если не предусмотрен овердрафт public decimal Balance { get; set; } } // Невалидное состояние: account.Balance == -100.00m -
Внутренняя противоречивость данных:
public class DateRange { public DateTime Start { get; set; } public DateTime End { get; set; } // Инвариант: End всегда >= Start } // Невалидное состояние: range.End < range.Start -
Неполная или некорректная инициализация:
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).