Domine o conceito que separa devs júnior de sênior
Aprenda a criar código que se adapta automaticamente. O conceito mais poderoso da orientação a objetos.
Uma função que funciona com qualquer implementação. Sem modificar código existente.
Elimina if/else gigantes. Cada classe cuida de si mesma.
Polimorfismo é obrigatório em entrevistas técnicas. Diferencial de carreira.
Cada dev implementa sua parte. Integração sem conflitos.
Vamos ver como NÃO fazer. Este código não usa polimorfismo.
// ❌ 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'
});
⚠️ Resultado: Código frágil, difícil de manter e impossível de escalar.
Agora vamos ver como fazer certo. Uma interface, múltiplas implementações.
// ✅ 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);
}
}
// ✅ 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);
}
}
// ✅ 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!
Veja como adicionar Apple Pay e PayPal SEM modificar código existente.
// ✅ 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);
}
}
// ✅ 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!
Exemplos práticos de polimorfismo em sistemas reais.
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.
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.
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.
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.
Como aplicar polimorfismo corretamente no seu código.
"Polimorfismo é a capacidade de um objeto assumir múltiplas formas" - Gang of Four