Ответ
Создание пользовательских классов исключений позволяет точно моделировать предметную область приложения и создавать четкую, понятную иерархию ошибок. Это ключевой инструмент для написания надежного и поддерживаемого кода.
Основные причины:
- Семантическая точность: Стандартные исключения (вроде
RuntimeExceptionилиInvalidArgumentException) часто слишком общие. Свои исключения точно описывают, что пошло не так (например,PaymentDeclinedException,UserNotFoundException,InvalidOrderStateException). - Гранулярная обработка: Позволяет перехватывать и обрабатывать разные типы сбоев по-разному.
- Добавление контекста: В кастомный класс можно добавить дополнительные свойства, которые помогут в логировании и отладке (ID транзакции, код ошибки внешнего API и т.д.).
- Улучшение читаемости кода: По имени класса исключения сразу понятна природа ошибки.
Пример на PHP:
<?php
// Базовое исключение для домена "Платежи"
namespace AppPaymentException;
class PaymentException extends RuntimeException {
protected string $transactionId;
public function __construct(string $message, string $transactionId, int $code = 0, ?Throwable $previous = null) {
parent::__construct($message, $code, $previous);
$this->transactionId = $transactionId;
}
public function getTransactionId(): string {
return $this->transactionId;
}
}
// Конкретные типы ошибок платежа
class InsufficientFundsException extends PaymentException {}
class CardDeclinedException extends PaymentException {}
class GatewayTimeoutException extends PaymentException {}
// Использование в коде
class PaymentProcessor {
public function charge(Order $order): void {
$transactionId = $this->generateId();
// ... логика проверки баланса
if (!$order->user->hasSufficientFunds($order->total)) {
throw new InsufficientFundsException(
"User does not have enough funds.",
$transactionId
);
}
// ... попытка списания
}
}
// Обработка на более высоком уровне
try {
$processor->charge($order);
} catch (InsufficientFundsException $e) {
// Конкретная обработка: показать сообщение пользователю, предложить другой способ
$this->logger->warning('Insufficient funds', ['transaction' => $e->getTransactionId()]);
$this->redirectToPaymentMethodSelection();
} catch (CardDeclinedException $e) {
// Другая обработка
} catch (PaymentException $e) {
// Общая обработка всех платежных ошибок
$this->logger->error('Payment failed', ['exception' => $e]);
throw new HttpException(500, 'Payment processing error');
}
Такой подход делает поток ошибок в приложении управляемым и понятным для разработчиков.