Ответ
Использование абстракций (интерфейсов и абстрактных классов) — это основа большинства паттернов проектирования в моей работе. Это позволяет создавать гибкий, расширяемый и тестируемый код.
1. Стратегия (Strategy) — самый частый случай:
В проекте по обработке платежей нужно было поддерживать множество платежных шлюзов (Stripe, PayPal, банковские карты). Вместо гигантского класса с кучей if-else я использовал паттерн Стратегия.
interface PaymentGateway {
public function charge(float $amount, array $customerData): PaymentResult;
public function refund(string $transactionId): bool;
}
class StripeGateway implements PaymentGateway {
public function charge(float $amount, array $customerData): PaymentResult {
// Логика вызова API Stripe
$charge = StripeCharge::create([...]);
return new PaymentResult($charge->id, $charge->status);
}
// ... метод refund
}
class PayPalGateway implements PaymentGateway {
// ... своя реализация
}
// Контекст, который использует абстракцию
class PaymentService {
private PaymentGateway $gateway;
public function __construct(PaymentGateway $gateway) {
$this->gateway = $gateway; // Внедряем конкретную стратегию
}
public function processOrder(Order $order): void {
$result = $this->gateway->charge($order->getTotal(), $order->getCustomerData());
$order->markAsPaid($result->getTransactionId());
}
}
// Использование. Выбор стратегии может происходить на основе конфига или данных заказа.
$gateway = new StripeGateway(); // Или PayPalGateway(), или другой
$paymentService = new PaymentService($gateway);
$paymentService->processOrder($order);
2. Фабричный метод (Factory Method) и Абстрактная фабрика: Использовал для создания семейств связанных объектов. Например, в системе отчетов были разные форматы вывода (PDF, Excel, HTML) и разные типы отчетов (финансовый, статистический). Абстрактная фабрика позволяла гарантировать, что для «Финансового отчета» будет создан корректный набор объектов (PDF-рендерер с финансовыми шаблонами).
3. Внедрение зависимостей (Dependency Injection): Это не столько паттерн, сколько практика, целиком построенная на абстракциях. Все мои сервисы в Symfony/Laravel проектах зависят от интерфейсов (репозиториев, клиентов внешних API, сервисов нотификации), а не от конкретных классов. Это позволяет:
- Легко тестировать: В тестах я подменяю реальную реализацию на Mock-объект, реализующий тот же интерфейс.
- Легко менять поведение: Чтобы перейти с одной облачной файловой системы (например, AWS S3) на другую (Google Cloud Storage), нужно лишь написать новую реализацию интерфейса
FileStorageInterfaceи изменить конфигурацию DI-контейнера.
Итог: Работа с абстракциями через паттерны — это инвестиция в поддерживаемость кода. Первоначальная сложность окупается в разы при необходимости модификации, расширения или тестирования системы.