← Назад к вопросам

Приведи пример использования Interface Segregation Principle

2.0 Middle🔥 71 комментариев
#Архитектура и паттерны#ООП

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Interface Segregation Principle (ISP) — практический пример

Interface Segregation Principle гласит: "Клиенты не должны зависеть от интерфейсов, которые они не используют". Вместо одного большого интерфейса лучше создать несколько маленьких, специализированных интерфейсов.

Проблема: монолитный интерфейс

Представим систему платежей для e-commerce приложения.

// ❌ Плохо — один большой интерфейс для всех платёжных методов
interface PaymentProcessor {
  processPayment(amount: number): Promise<void>;
  refund(amount: number): Promise<void>;
  getBalance(): Promise<number>;
  setupRecurringPayment(interval: string): Promise<void>;
  validateCardDetails(card: CardInfo): boolean;
  authenticate2FA(code: string): Promise<void>;
  getTransactionHistory(): Promise<Transaction[]>;
}

// Когда мы реализуем этот интерфейс для разных методов платежа,
// появляются проблемы:

class BitcoinPayment implements PaymentProcessor {
  processPayment(amount: number): Promise<void> {
    // Криптовалюта может обработать платёж
  }

  refund(amount: number): Promise<void> {
    // Но возврат сложнее — обычно нет возврата в крипто
    throw new Error('Bitcoin does not support refunds');
  }

  getBalance(): Promise<number> {
    // Баланс? Крипто кошелька или что?
  }

  setupRecurringPayment(interval: string): Promise<void> {
    // Крипто не поддерживает автоматические платежи
    throw new Error('Not supported');
  }

  validateCardDetails(card: CardInfo): boolean {
    // У крипто нет карточек!
    throw new Error('Not applicable');
  }

  authenticate2FA(code: string): Promise<void> {
    // Может и не нужна
    throw new Error('Not implemented');
  }

  getTransactionHistory(): Promise<Transaction[]> {
    // Может быть
  }
}

Проблемы этого подхода:

  • Классы вынуждены реализовывать ненужные методы
  • Нарушается принцип единственной ответственности
  • Клиентский код должен знать обо всех методах, даже если он их не использует
  • Сложнее тестировать и поддерживать

Решение: разделить интерфейсы

// ✅ Хорошо — специализированные интерфейсы

// Базовый интерфейс — все платежи должны это делать
interface BasicPayment {
  processPayment(amount: number): Promise<void>;
}

// Опциональные возможности
interface Refundable {
  refund(amount: number): Promise<void>;
}

interface BalanceProvider {
  getBalance(): Promise<number>;
}

interface RecurringPaymentSupport {
  setupRecurringPayment(interval: string): Promise<void>;
  cancelRecurringPayment(): Promise<void>;
}

interface CardValidation {
  validateCardDetails(card: CardInfo): boolean;
}

interface TwoFactorAuth {
  authenticate2FA(code: string): Promise<void>;
}

interface TransactionHistory {
  getTransactionHistory(): Promise<Transaction[]>;
}

// Теперь каждый платёжный метод реализует только нужные ему интерфейсы

class CreditCardPayment implements 
  BasicPayment, 
  Refundable, 
  BalanceProvider, 
  RecurringPaymentSupport, 
  CardValidation, 
  TwoFactorAuth, 
  TransactionHistory {
  
  async processPayment(amount: number): Promise<void> {
    console.log(`Processing credit card payment: $${amount}`);
    // логика обработки
  }

  async refund(amount: number): Promise<void> {
    console.log(`Refunding credit card payment: $${amount}`);
  }

  async getBalance(): Promise<number> {
    return 5000; // кредитный лимит
  }

  async setupRecurringPayment(interval: string): Promise<void> {
    console.log(`Setting up recurring payment every ${interval}`);
  }

  cancelRecurringPayment(): Promise<void> {
    console.log('Cancelling recurring payment');
    return Promise.resolve();
  }

  validateCardDetails(card: CardInfo): boolean {
    return card.cvv && card.expiryDate && card.cardNumber;
  }

  async authenticate2FA(code: string): Promise<void> {
    console.log(`Authenticating with 2FA code: ${code}`);
  }

  async getTransactionHistory(): Promise<Transaction[]> {
    return [];
  }
}

class BitcoinPayment implements BasicPayment, BalanceProvider, TransactionHistory {
  async processPayment(amount: number): Promise<void> {
    console.log(`Processing bitcoin payment: ${amount} BTC`);
  }

  async getBalance(): Promise<number> {
    return 0.5; // баланс в BTC
  }

  async getTransactionHistory(): Promise<Transaction[]> {
    return []; // данные из блокчейна
  }
  // Остальные методы НЕ реализуем — они не нужны для крипто
}

class ApplePayment implements BasicPayment, Refundable, TwoFactorAuth {
  async processPayment(amount: number): Promise<void> {
    console.log(`Processing Apple Pay: $${amount}`);
  }

  async refund(amount: number): Promise<void> {
    console.log(`Refunding Apple Pay: $${amount}`);
  }

  async authenticate2FA(code: string): Promise<void> {
    console.log('Biometric authentication through Apple Pay');
  }
}

Использование в клиентском коде

// Теперь функции получают только нужные интерфейсы

// Функция, которая обрабатывает платёжи
function processUserPayment(processor: BasicPayment, amount: number): void {
  processor.processPayment(amount);
  // Она не знает о других методах и не зависит от них
}

// Функция, которая делает возвраты
function processRefund(processor: Refundable, amount: number): void {
  processor.refund(amount);
  // Можно передать только платежи, которые поддерживают возврат
}

// Функция для двухфакторной аутентификации
function authenticate(processor: TwoFactorAuth, code: string): void {
  processor.authenticate2FA(code);
  // Работает только с платежами, которые это поддерживают
}

// Использование
const creditCard = new CreditCardPayment();
const bitcoin = new BitcoinPayment();

processUserPayment(creditCard, 100); // OK
processUserPayment(bitcoin, 0.005);   // OK

processRefund(creditCard, 50);        // OK
processRefund(bitcoin, 0.002);        // ❌ Ошибка типа — Bitcoin не поддерживает refund!

authenticate(creditCard, '123456');   // OK
authenticate(bitcoin, '123456');      // ❌ Ошибка типа — Bitcoin не имеет 2FA!

Реальный пример в NestJS

// services/payment.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class PaymentService {
  // Сервис только обрабатывает платежи
  async processPayment(processor: BasicPayment, amount: number): Promise<void> {
    await processor.processPayment(amount);
  }
}

// services/refund.service.ts
@Injectable()
export class RefundService {
  // Сервис возвратов работает только с поддерживающими возврат
  async processRefund(processor: Refundable, amount: number): Promise<void> {
    await processor.refund(amount);
  }
}

// services/recurring-payment.service.ts
@Injectable()
export class RecurringPaymentService {
  async setupRecurring(processor: RecurringPaymentSupport, interval: string): Promise<void> {
    await processor.setupRecurringPayment(interval);
  }
}

// controller
@Controller('payments')
export class PaymentController {
  constructor(
    private paymentService: PaymentService,
    private refundService: RefundService,
    private recurringService: RecurringPaymentService,
  ) {}

  @Post('process')
  async process(@Body() { processor, amount }: ProcessPaymentDto) {
    // Каждый сервис работает со своим специализированным интерфейсом
    return this.paymentService.processPayment(processor, amount);
  }

  @Post('refund')
  async refund(@Body() { processor, amount }: RefundDto) {
    return this.refundService.processRefund(processor, amount);
  }
}

Преимущества ISP

Гибкость — классы реализуют только нужный функционал ✅ Меньше связанности — клиентский код не зависит от ненужных методов ✅ Проще тестировать — можем создавать моки только нужных интерфейсов ✅ Проще расширять — добавление нового платёжного метода не требует реализации всех методов ✅ Понятнее типизация — TypeScript покажет ошибку, если метод не поддерживается

Итоги

Interface Segregation Principle — это про то, чтобы:

  • Не заставлять классы реализовывать ненужное
  • Не заставлять клиентов знать о ненужных методах
  • Создавать маленькие, специализированные интерфейсы
  • Строить гибкую и поддерживаемую архитектуру