Как реализовывал принципы SOLID на практике?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Реализация принципов SOLID на практике
Принципы SOLID — это фундамент чистого архитектурного дизайна, и я применял их постоянно в реальных проектах Node.js.
Single Responsibility Principle (SRP)
В одном из микросервисов обработки платежей я выделил отдельные классы для каждой ответственности:
// ❌ Плохо — множественная ответственность
class PaymentHandler {
async process(payment) {
// валидация
// обработка платежа
// отправка письма
// логирование
// обновление БД
}
}
// ✅ Хорошо — каждый класс отвечает за одно
class PaymentProcessor {
async process(payment) {
// только обработка платежа
}
}
class EmailNotificationService {
async sendReceipt(email, data) {
// только отправка писем
}
}
class PaymentLogger {
async log(transactionId, status) {
// только логирование
}
}
Open/Closed Principle (OCP)
При разработке системы различных способов оплаты я использовал абстрактный класс:
abstract class PaymentProvider {
abstract async charge(amount: number): Promise<void>;
abstract async refund(transactionId: string): Promise<void>;
}
class StripeProvider extends PaymentProvider {
async charge(amount: number) { /* Stripe логика */ }
async refund(transactionId: string) { /* Stripe возвраты */ }
}
class PayPalProvider extends PaymentProvider {
async charge(amount: number) { /* PayPal логика */ }
async refund(transactionId: string) { /* PayPal возвраты */ }
}
Таким образом, новый провайдер добавляется без изменения существующего кода.
Liskov Substitution Principle (LSP)
Все провайдеры заменяемы друг на друга благодаря единому интерфейсу:
class PaymentService {
constructor(private provider: PaymentProvider) {}
async processPayment(amount: number) {
return this.provider.charge(amount);
}
}
// Работает с любым провайдером
const stripeService = new PaymentService(new StripeProvider());
const paypalService = new PaymentService(new PayPalProvider());
Interface Segregation Principle (ISP)
Вместо одного толстого интерфейса я разделил на несколько узких:
// ❌ Плохо — клиент зависит от ненужных методов
interface PaymentProvider {
charge(): void;
refund(): void;
validateCard(): void;
storeCrypto(): void;
}
// ✅ Хорошо
interface Chargeable {
charge(amount: number): Promise<void>;
}
interface Refundable {
refund(transactionId: string): Promise<void>;
}
class CryptoBridgeProvider implements Chargeable, Refundable { }
Dependency Inversion Principle (DIP)
В высокоуровневых модулях зависимости от низкоуровневых передаются через конструктор:
class OrderService {
constructor(
private paymentProvider: PaymentProvider,
private notificationService: EmailNotificationService,
private logger: Logger
) {}
async createOrder(order) {
await this.paymentProvider.charge(order.amount);
await this.logger.log("Order created");
await this.notificationService.sendConfirmation(order.email);
}
}
На практике это позволило нам легко тестировать через mock-объекты, быстро переключаться между провайдерами и поддерживать код в чистоте на протяжении всей разработки.