🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
OOP Avançado

Uma interface, múltiplas implementações Polimorfismo TypeScript

Domine o conceito que separa devs júnior de sênior

Tutorial Avançado

O poder de uma interface, múltiplas formas Polimorfismo TypeScript

Aprenda a criar código que se adapta automaticamente. O conceito mais poderoso da orientação a objetos.

1
Interface
Implementações
0
If/Else
polymorphism.ts
// ❌ Sem polimorfismo function processPayment(type: string, amount: number) { if (type === 'credit') { /* lógica cartão */ } else if (type === 'pix') { /* lógica pix */ } else if (type === 'boleto') { /* lógica boleto */ } } // ✅ Com polimorfismo interface PaymentMethod { process(amount: number): Promise<boolean>; } function processPayment(method: PaymentMethod, amount: number) { return method.process(amount); // Magia! }

Por que isso é importante

Código que se adapta

Uma função que funciona com qualquer implementação. Sem modificar código existente.

Menos bugs, mais velocidade

Elimina if/else gigantes. Cada classe cuida de si mesma.

Requisito para sênior

Polimorfismo é obrigatório em entrevistas técnicas. Diferencial de carreira.

Trabalho em equipe

Cada dev implementa sua parte. Integração sem conflitos.

O Problema: Código Rígido

Vamos ver como NÃO fazer. Este código não usa polimorfismo.

❌ Sistema de Pagamento Rígido

// ❌ PÉSSIMA abordagem - sem polimorfismo
class PaymentProcessor {
  processPayment(type: string, amount: number, data: any): boolean {
    switch (type) {
      case 'credit_card':
        // Validar cartão
        if (!data.number || !data.cvv || !data.expiry) {
          return false;
        }
        // Processar com gateway de cartão
        console.log(`Processando R$ ${amount} no cartão ${data.number.slice(-4)}`);
        return true;
        
      case 'pix':
        // Validar PIX
        if (!data.key) {
          return false;
        }
        // Processar PIX
        console.log(`Processando R$ ${amount} via PIX para ${data.key}`);
        return true;
        
      case 'boleto':
        // Validar boleto
        if (!data.dueDate) {
          return false;
        }
        // Gerar boleto
        console.log(`Gerando boleto de R$ ${amount} com vencimento ${data.dueDate}`);
        return true;
        
      case 'paypal':
        // Validar PayPal
        if (!data.email) {
          return false;
        }
        // Processar PayPal
        console.log(`Processando R$ ${amount} via PayPal para ${data.email}`);
        return true;
        
      default:
        throw new Error(`Método de pagamento ${type} não suportado`);
    }
  }
  
  // Método para calcular taxas - MAIS if/else!
  calculateFee(type: string, amount: number): number {
    switch (type) {
      case 'credit_card':
        return amount * 0.035; // 3.5%
      case 'pix':
        return 0; // Grátis
      case 'boleto':
        return 2.50; // Taxa fixa
      case 'paypal':
        return amount * 0.045; // 4.5%
      default:
        return 0;
    }
  }
}

// Uso problemático
const processor = new PaymentProcessor();

// Precisa saber os detalhes de cada tipo
processor.processPayment('credit_card', 100, {
  number: '1234567890123456',
  cvv: '123',
  expiry: '12/25'
});

processor.processPayment('pix', 50, {
  key: 'user@email.com'
});

Problemas desta abordagem:

  • • Switch/case gigante e difícil de manter
  • • Precisa modificar a classe para cada novo método
  • • Viola o Open-Closed Principle
  • • Difícil de testar isoladamente
  • • Acoplamento forte entre tipos
  • • Código cresce descontroladamente

Cenário real:

  • • Cliente quer Apple Pay
  • • Precisa modificar o switch
  • • Risco de quebrar métodos existentes
  • • Testes precisam ser refeitos
  • • Deploy com medo
  • • Código vira um monstro

⚠️ Resultado: Código frágil, difícil de manter e impossível de escalar.

A Solução: Polimorfismo

Agora vamos ver como fazer certo. Uma interface, múltiplas implementações.

✅ Sistema de Pagamento Polimórfico

1. Interface Base

// ✅ Contrato que todos devem seguir
interface PaymentMethod {
  process(amount: number): Promise<PaymentResult>;
  calculateFee(amount: number): number;
  validate(): boolean;
  getMethodName(): string;
}

// Tipos auxiliares
interface PaymentResult {
  success: boolean;
  transactionId?: string;
  errorMessage?: string;
}

// Classe base opcional com funcionalidades comuns
abstract class BasePaymentMethod implements PaymentMethod {
  abstract process(amount: number): Promise<PaymentResult>;
  abstract calculateFee(amount: number): number;
  abstract validate(): boolean;
  abstract getMethodName(): string;
  
  // Método comum para logging
  protected log(action: string, amount: number, success: boolean): void {
    const status = success ? 'SUCCESS' : 'FAILED';
    console.log(`[${this.getMethodName()}] ${action} R$ ${amount}: ${status}`);
  }
  
  // Método comum para formatação
  protected formatAmount(amount: number): string {
    return new Intl.NumberFormat('pt-BR', {
      style: 'currency',
      currency: 'BRL'
    }).format(amount);
  }
}

2. Implementações Específicas

// ✅ Cartão de Crédito
class CreditCardPayment extends BasePaymentMethod {
  constructor(
    private cardNumber: string,
    private cvv: string,
    private expiryDate: string,
    private holderName: string
  ) {
    super();
  }
  
  async process(amount: number): Promise<PaymentResult> {
    if (!this.validate()) {
      return { success: false, errorMessage: 'Dados do cartão inválidos' };
    }
    
    try {
      // Simulação de processamento
      await this.processWithGateway(amount);
      const transactionId = this.generateTransactionId();
      
      this.log('Payment processed', amount, true);
      return { success: true, transactionId };
    } catch (error) {
      this.log('Payment failed', amount, false);
      return { success: false, errorMessage: 'Erro no processamento' };
    }
  }
  
  calculateFee(amount: number): number {
    return amount * 0.035; // 3.5%
  }
  
  validate(): boolean {
    return this.cardNumber.length === 16 &&
           this.cvv.length === 3 &&
           this.expiryDate.match(/^d{2}/d{2}$/) !== null &&
           this.holderName.length > 0;
  }
  
  getMethodName(): string {
    return 'CREDIT_CARD';
  }
  
  private async processWithGateway(amount: number): Promise<void> {
    // Integração com gateway de pagamento
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
  
  private generateTransactionId(): string {
    return 'CC_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }
}

// ✅ PIX
class PixPayment extends BasePaymentMethod {
  constructor(private pixKey: string) {
    super();
  }
  
  async process(amount: number): Promise<PaymentResult> {
    if (!this.validate()) {
      return { success: false, errorMessage: 'Chave PIX inválida' };
    }
    
    try {
      const qrCode = await this.generateQRCode(amount);
      const transactionId = this.generateTransactionId();
      
      this.log('PIX QR Code generated', amount, true);
      return { success: true, transactionId };
    } catch (error) {
      this.log('PIX generation failed', amount, false);
      return { success: false, errorMessage: 'Erro na geração do PIX' };
    }
  }
  
  calculateFee(amount: number): number {
    return 0; // PIX é gratuito
  }
  
  validate(): boolean {
    // Validação simplificada de chave PIX
    return this.pixKey.length > 0 &&
           (this.pixKey.includes('@') || // Email
            this.pixKey.match(/^d{11}$/) || // CPF
            this.pixKey.match(/^d{14}$/) || // CNPJ
            this.pixKey.match(/^+d{13}$/)); // Telefone
  }
  
  getMethodName(): string {
    return 'PIX';
  }
  
  private async generateQRCode(amount: number): Promise<string> {
    // Simulação de geração de QR Code
    await new Promise(resolve => setTimeout(resolve, 500));
    return `PIX_QR_${amount}_${this.pixKey}`;
  }
  
  private generateTransactionId(): string {
    return 'PIX_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }
}

// ✅ Boleto
class BoletoPayment extends BasePaymentMethod {
  constructor(
    private dueDate: Date,
    private payerDocument: string,
    private payerName: string
  ) {
    super();
  }
  
  async process(amount: number): Promise<PaymentResult> {
    if (!this.validate()) {
      return { success: false, errorMessage: 'Dados do boleto inválidos' };
    }
    
    try {
      const boletoCode = await this.generateBoleto(amount);
      const transactionId = this.generateTransactionId();
      
      this.log('Boleto generated', amount, true);
      return { success: true, transactionId };
    } catch (error) {
      this.log('Boleto generation failed', amount, false);
      return { success: false, errorMessage: 'Erro na geração do boleto' };
    }
  }
  
  calculateFee(amount: number): number {
    return 2.50; // Taxa fixa
  }
  
  validate(): boolean {
    const now = new Date();
    return this.dueDate > now &&
           this.payerDocument.length >= 11 &&
           this.payerName.length > 0;
  }
  
  getMethodName(): string {
    return 'BOLETO';
  }
  
  private async generateBoleto(amount: number): Promise<string> {
    // Simulação de geração de boleto
    await new Promise(resolve => setTimeout(resolve, 800));
    return `BOLETO_${amount}_${this.dueDate.getTime()}`;
  }
  
  private generateTransactionId(): string {
    return 'BOL_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }
}

3. Processador Polimórfico

// ✅ Processador que usa polimorfismo
class PaymentProcessor {
  // ZERO if/else ou switch/case!
  async processPayment(method: PaymentMethod, amount: number): Promise<PaymentResult> {
    console.log(`Iniciando pagamento de ${method.formatAmount ? method.formatAmount(amount) : amount} via ${method.getMethodName()}`);
    
    // Calcula taxa
    const fee = method.calculateFee(amount);
    const totalAmount = amount + fee;
    
    console.log(`Taxa: R$ ${fee} | Total: R$ ${totalAmount}`);
    
    // Processa pagamento - POLIMORFISMO EM AÇÃO!
    const result = await method.process(totalAmount);
    
    if (result.success) {
      console.log(`✅ Pagamento aprovado! ID: ${result.transactionId}`);
    } else {
      console.log(`❌ Pagamento rejeitado: ${result.errorMessage}`);
    }
    
    return result;
  }
  
  // Processa múltiplos pagamentos
  async processMultiplePayments(payments: Array<{method: PaymentMethod, amount: number}>): Promise<PaymentResult[]> {
    const results = await Promise.all(
      payments.map(({method, amount}) => this.processPayment(method, amount))
    );
    
    const successful = results.filter(r => r.success).length;
    console.log(`
📊 Resumo: ${successful}/${results.length} pagamentos aprovados`);
    
    return results;
  }
  
  // Calcula total de taxas
  calculateTotalFees(payments: Array<{method: PaymentMethod, amount: number}>): number {
    return payments.reduce((total, {method, amount}) => {
      return total + method.calculateFee(amount);
    }, 0);
  }
}

// ✅ Uso limpo e extensível
const processor = new PaymentProcessor();

// Criando métodos de pagamento
const creditCard = new CreditCardPayment(
  '1234567890123456',
  '123',
  '12/25',
  'João Silva'
);

const pix = new PixPayment('joao@email.com');

const boleto = new BoletoPayment(
  new Date('2025-02-15'),
  '12345678901',
  'João Silva'
);

// Processando pagamentos - MESMO CÓDIGO para todos os tipos!
await processor.processPayment(creditCard, 100);
await processor.processPayment(pix, 50);
await processor.processPayment(boleto, 200);

// Processamento em lote
await processor.processMultiplePayments([
  { method: creditCard, amount: 100 },
  { method: pix, amount: 50 },
  { method: boleto, amount: 200 }
]);

✅ Resultado: Código limpo, extensível e sem if/else. Cada método cuida de si mesmo!

Extensão: Novos Métodos

Veja como adicionar Apple Pay e PayPal SEM modificar código existente.

🚀 Extensão Sem Modificação

Novos métodos de pagamento:

// ✅ Apple Pay - NOVA implementação
class ApplePayPayment extends BasePaymentMethod {
  constructor(
    private deviceId: string,
    private touchId: boolean
  ) {
    super();
  }
  
  async process(amount: number): Promise<PaymentResult> {
    if (!this.validate()) {
      return { success: false, errorMessage: 'Apple Pay não configurado' };
    }
    
    try {
      // Autenticação biométrica
      const authResult = await this.authenticateWithTouchId();
      if (!authResult) {
        return { success: false, errorMessage: 'Falha na autenticação' };
      }
      
      // Processamento via Apple Pay
      await this.processWithApple(amount);
      const transactionId = this.generateTransactionId();
      
      this.log('Apple Pay processed', amount, true);
      return { success: true, transactionId };
    } catch (error) {
      this.log('Apple Pay failed', amount, false);
      return { success: false, errorMessage: 'Erro no Apple Pay' };
    }
  }
  
  calculateFee(amount: number): number {
    return amount * 0.025; // 2.5% - menor que cartão
  }
  
  validate(): boolean {
    return this.deviceId.length > 0 && this.touchId;
  }
  
  getMethodName(): string {
    return 'APPLE_PAY';
  }
  
  private async authenticateWithTouchId(): Promise<boolean> {
    // Simulação de autenticação biométrica
    await new Promise(resolve => setTimeout(resolve, 1500));
    return Math.random() > 0.1; // 90% de sucesso
  }
  
  private async processWithApple(amount: number): Promise<void> {
    await new Promise(resolve => setTimeout(resolve, 800));
  }
  
  private generateTransactionId(): string {
    return 'APPLE_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }
}

// ✅ PayPal - NOVA implementação
class PayPalPayment extends BasePaymentMethod {
  constructor(
    private email: string,
    private password: string
  ) {
    super();
  }
  
  async process(amount: number): Promise<PaymentResult> {
    if (!this.validate()) {
      return { success: false, errorMessage: 'Credenciais PayPal inválidas' };
    }
    
    try {
      // Login no PayPal
      const loginResult = await this.loginToPayPal();
      if (!loginResult) {
        return { success: false, errorMessage: 'Falha no login PayPal' };
      }
      
      // Processamento via PayPal
      await this.processWithPayPal(amount);
      const transactionId = this.generateTransactionId();
      
      this.log('PayPal processed', amount, true);
      return { success: true, transactionId };
    } catch (error) {
      this.log('PayPal failed', amount, false);
      return { success: false, errorMessage: 'Erro no PayPal' };
    }
  }
  
  calculateFee(amount: number): number {
    return amount * 0.045; // 4.5%
  }
  
  validate(): boolean {
    return this.email.includes('@') && this.password.length >= 6;
  }
  
  getMethodName(): string {
    return 'PAYPAL';
  }
  
  private async loginToPayPal(): Promise<boolean> {
    // Simulação de login
    await new Promise(resolve => setTimeout(resolve, 1200));
    return Math.random() > 0.05; // 95% de sucesso
  }
  
  private async processWithPayPal(amount: number): Promise<void> {
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
  
  private generateTransactionId(): string {
    return 'PP_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }
}

// ✅ Criptomoeda - NOVA implementação
class CryptoPayment extends BasePaymentMethod {
  constructor(
    private walletAddress: string,
    private cryptoType: 'BTC' | 'ETH' | 'ADA'
  ) {
    super();
  }
  
  async process(amount: number): Promise<PaymentResult> {
    if (!this.validate()) {
      return { success: false, errorMessage: 'Endereço de wallet inválido' };
    }
    
    try {
      // Conversão para crypto
      const cryptoAmount = await this.convertToCrypto(amount);
      
      // Processamento na blockchain
      await this.processOnBlockchain(cryptoAmount);
      const transactionId = this.generateTransactionId();
      
      this.log(`${this.cryptoType} processed`, amount, true);
      return { success: true, transactionId };
    } catch (error) {
      this.log(`${this.cryptoType} failed`, amount, false);
      return { success: false, errorMessage: 'Erro na blockchain' };
    }
  }
  
  calculateFee(amount: number): number {
    // Taxa varia por tipo de crypto
    const feeRates = { BTC: 0.001, ETH: 0.002, ADA: 0.0005 };
    return amount * feeRates[this.cryptoType];
  }
  
  validate(): boolean {
    // Validação simplificada de endereço
    return this.walletAddress.length >= 26 && this.walletAddress.length <= 62;
  }
  
  getMethodName(): string {
    return `CRYPTO_${this.cryptoType}`;
  }
  
  private async convertToCrypto(amount: number): Promise<number> {
    // Simulação de conversão
    await new Promise(resolve => setTimeout(resolve, 2000));
    const rates = { BTC: 0.000002, ETH: 0.0003, ADA: 0.5 };
    return amount * rates[this.cryptoType];
  }
  
  private async processOnBlockchain(cryptoAmount: number): Promise<void> {
    // Simulação de transação blockchain
    await new Promise(resolve => setTimeout(resolve, 3000));
  }
  
  private generateTransactionId(): string {
    return `${this.cryptoType}_` + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }
}

Uso dos novos métodos:

// ✅ O PaymentProcessor funciona SEM modificação!
const processor = new PaymentProcessor();

// Novos métodos
const applePay = new ApplePayPayment('iPhone_12_Pro', true);
const paypal = new PayPalPayment('user@email.com', 'senha123');
const bitcoin = new CryptoPayment('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', 'BTC');

// MESMO código do processador funciona com TODOS os métodos!
await processor.processPayment(applePay, 150);
await processor.processPayment(paypal, 75);
await processor.processPayment(bitcoin, 300);

// Processamento em lote com TODOS os métodos
const allMethods = [
  { method: new CreditCardPayment('1234567890123456', '123', '12/25', 'João'), amount: 100 },
  { method: new PixPayment('joao@email.com'), amount: 50 },
  { method: new BoletoPayment(new Date('2025-02-15'), '12345678901', 'João'), amount: 200 },
  { method: applePay, amount: 150 },
  { method: paypal, amount: 75 },
  { method: bitcoin, amount: 300 }
];

await processor.processMultiplePayments(allMethods);

// Calculando taxas totais
const totalFees = processor.calculateTotalFees(allMethods);
console.log(`💰 Total de taxas: R$ ${totalFees}`);

// Factory pattern para criar métodos dinamicamente
class PaymentMethodFactory {
  static create(type: string, data: any): PaymentMethod {
    switch (type) {
      case 'credit_card':
        return new CreditCardPayment(data.number, data.cvv, data.expiry, data.name);
      case 'pix':
        return new PixPayment(data.key);
      case 'boleto':
        return new BoletoPayment(data.dueDate, data.document, data.name);
      case 'apple_pay':
        return new ApplePayPayment(data.deviceId, data.touchId);
      case 'paypal':
        return new PayPalPayment(data.email, data.password);
      case 'crypto':
        return new CryptoPayment(data.wallet, data.type);
      default:
        throw new Error(`Método ${type} não suportado`);
    }
  }
}

// Uso da factory
const dynamicMethod = PaymentMethodFactory.create('apple_pay', {
  deviceId: 'iPhone_13',
  touchId: true
});

await processor.processPayment(dynamicMethod, 99);

🎯 Magia: Adicionamos 3 novos métodos de pagamento sem tocar em UMA linha do código original!

Casos Reais: Onde Usar Polimorfismo

Exemplos práticos de polimorfismo em sistemas reais.

📧 Sistema de Notificações

interface NotificationSender {
  send(message: string, recipient: string): Promise<boolean>;
}

class EmailSender implements NotificationSender {
  async send(message: string, email: string) {
    // Envio por email
    return true;
  }
}

class SMSSender implements NotificationSender {
  async send(message: string, phone: string) {
    // Envio por SMS
    return true;
  }
}

class PushSender implements NotificationSender {
  async send(message: string, deviceToken: string) {
    // Push notification
    return true;
  }
}

// Uso polimórfico
class NotificationService {
  async notify(sender: NotificationSender, message: string, recipient: string) {
    return await sender.send(message, recipient);
  }
}

Adicione WhatsApp, Telegram, Discord sem modificar o serviço.

💾 Sistema de Storage

interface FileStorage {
  upload(file: File, path: string): Promise<string>;
  download(path: string): Promise<File>;
  delete(path: string): Promise<boolean>;
}

class S3Storage implements FileStorage {
  async upload(file: File, path: string) {
    // Upload para AWS S3
    return 'https://s3.amazonaws.com/' + path;
  }
  // ... outros métodos
}

class GoogleCloudStorage implements FileStorage {
  async upload(file: File, path: string) {
    // Upload para Google Cloud
    return 'https://storage.googleapis.com/' + path;
  }
  // ... outros métodos
}

class LocalStorage implements FileStorage {
  async upload(file: File, path: string) {
    // Salva localmente
    return '/uploads/' + path;
  }
  // ... outros métodos
}

Troque de AWS para Google Cloud sem alterar sua aplicação.

🔐 Sistema de Autenticação

interface AuthProvider {
  authenticate(credentials: any): Promise<User | null>;
  logout(user: User): Promise<boolean>;
}

class GoogleAuthProvider implements AuthProvider {
  async authenticate(googleToken: string) {
    // Autenticação via Google
    return new User('google_user');
  }
  // ... outros métodos
}

class FacebookAuthProvider implements AuthProvider {
  async authenticate(fbToken: string) {
    // Autenticação via Facebook
    return new User('fb_user');
  }
  // ... outros métodos
}

class EmailPasswordAuthProvider implements AuthProvider {
  async authenticate({email, password}: {email: string, password: string}) {
    // Autenticação tradicional
    return new User('email_user');
  }
  // ... outros métodos
}

// Uso
class AuthService {
  async login(provider: AuthProvider, credentials: any) {
    return await provider.authenticate(credentials);
  }
}

Adicione GitHub, LinkedIn, Apple ID facilmente.

📊 Sistema de Relatórios

interface ReportGenerator {
  generate(data: any[]): Promise<string>;
  getFormat(): string;
}

class PDFReportGenerator implements ReportGenerator {
  async generate(data: any[]) {
    // Gera PDF
    return 'report.pdf';
  }
  getFormat() { return 'PDF'; }
}

class ExcelReportGenerator implements ReportGenerator {
  async generate(data: any[]) {
    // Gera Excel
    return 'report.xlsx';
  }
  getFormat() { return 'EXCEL'; }
}

class CSVReportGenerator implements ReportGenerator {
  async generate(data: any[]) {
    // Gera CSV
    return 'report.csv';
  }
  getFormat() { return 'CSV'; }
}

// Uso
class ReportService {
  async createReport(generator: ReportGenerator, data: any[]) {
    console.log(`Gerando relatório em ${generator.getFormat()}`);
    return await generator.generate(data);
  }
}

Adicione JSON, XML, PowerPoint sem modificar o serviço.

Checklist de Polimorfismo

Como aplicar polimorfismo corretamente no seu código.

✅ Faça Isso

  • Defina interfaces claras
  • Use classes abstratas quando necessário
  • Implemente todos os métodos da interface
  • Mantenha comportamento consistente
  • Use dependency injection
  • Teste cada implementação isoladamente

❌ Evite Isso

  • Switch/case baseado em tipo
  • If/else em cadeia
  • Métodos que lançam NotImplementedError
  • Verificação de tipo em runtime
  • Interfaces muito específicas
  • Acoplamento com implementações concretas

🎯 Resumo do Polimorfismo

1
Interface
Define o contrato
Implementações
Cada uma com sua lógica
0
If/Else
Código limpo e extensível

"Polimorfismo é a capacidade de um objeto assumir múltiplas formas" - Gang of Four

Domine OOP em TypeScript

Aprenda orientação a objetos do básico ao avançado com projetos reais.