Regra
Favor composição em vez de herança
Profundo herança hierarquias criar um e
e tornam o código mais de compreender e manter.
Idiomas suportados: 45+Introdução
Herança cria um acoplamento forte entre classes pai e filho, tornando o código frágil e difícil de mudar. Quando uma classe herda comportamento, ela se torna dependente dos detalhes de implementação de seu pai. Subclasses que sobrescrevem métodos mas ainda chamam super são particularmente problemáticos, misturando sua própria lógica com o comportamento herdado de maneiras que quebram quando o pai muda. A composição resolve isso permitindo que os objetos deleguem a outros objetos, criando um acoplamento fraco e uma clara separação de preocupações.
Por que isso importa
Preocupações diversas e alto acoplamento: Herança força preocupações não relacionadas na mesma hierarquia de classes. Uma classe de pagamento recorrente que herda de um processador de pagamentos mistura lógica de agendamento com processamento de pagamentos. Quando você precisa chamar super.process() e então adicionar seu próprio comportamento, você estará fortemente acoplado à implementação do pai. Se o do pai process() o método muda, a classe filha quebra de maneiras inesperadas.
Herdando comportamento indesejado: Subclasses herdam tudo de suas classes pai, incluindo métodos que não precisam ou que exigem implementações diferentes. Um pagamento recorrente herda refund() lógica projetada para pagamentos únicos, mas os reembolsos de assinatura funcionam de forma diferente. Você ou sobrescreve métodos e cria confusão, ou convive com um comportamento herdado inadequado.
Problema da classe base frágil: Alterações em classes pai se propagam por todas as subclasses. Modificando como CreditCardPayment processa pagamentos afeta Pagamento Recorrente com Cartão de Crédito mesmo que a alteração seja irrelevante para o agendamento. Isso torna o refactoring perigoso porque você não pode prever quais subclasses serão afetadas.
Complexidade de teste: Testar classes profundamente em uma hierarquia de herança requer a compreensão do comportamento da classe pai. Para testar o agendamento de pagamentos recorrentes, você também deve lidar com a lógica de processamento de cartão de crédito, chamadas de API do Stripe e validação. A composição permite testar o agendamento com um objeto de pagamento mock simples.
Exemplos de código
❌ Não-conforme:
class Payment {
constructor(amount, currency) {
this.amount = amount;
this.currency = currency;
}
async process() {
throw new Error('Must implement in subclass');
}
async refund() {
throw new Error('Must implement in subclass');
}
async sendReceipt(email) {
// All paymet types need receipts
await emailService.send(email, this.buildReceipt());
}
}
class CreditCardPayment extends Payment {
constructor(amount, currency, cardToken, billingAddress) {
super(amount, currency);
this.cardToken = cardToken;
this.billingAddress = billingAddress;
}
async process() {
await this.validateCard();
return await stripe.charges.create({
amount: this.amount * 100,
source: this.cardToken,
currency: this.currency
});
}
async refund() {
await this.validateRefund();
return await stripe.refunds.create({ charge: this.chargeId });
}
async validateCard() {
// Card validation logic
}
}
// Problem: RecurringCreditCardPayment's main concern is dealing with scheduling
// and not the actual payment
class RecurringCreditCardPayment extends CreditCardPayment {
constructor(amount, currency, cardToken, billingAddress, schedule) {
super(amount, currency, cardToken, billingAddress);
this.schedule = schedule;
}
async process() {
// Problem: Need to override parent's process() but also use it
await super.process();
await this.scheduleNextPayment();
}
async scheduleNextPayment() {
// Subscription scheduling
}
// Problem: Inherits refund() from parent but refunding
// subscriptions needs different logic
}Por que está errado: Pagamento Recorrente com Cartão de Crédito herda a lógica de processamento de pagamentos, mas sua verdadeira preocupação é o agendamento, não os pagamentos. Ele deve chamar super.process() e envolvê-lo com comportamento de agendamento, criando um acoplamento forte. A classe herda refund() da entidade principal, mas o reembolso de assinaturas requer uma lógica diferente de pagamentos únicos. Alterações em CreditCardPayment afetar Pagamento Recorrente com Cartão de Crédito mesmo quando essas alterações são irrelevantes para o agendamento.
✅ Compatível:
class CreditCardPayment extends Payment {
constructor(amount, currency, cardToken, billingAddress) {
super(amount, currency);
this.cardToken = cardToken;
this.billingAddress = billingAddress;
}
async process() {
await this.validateCard();
return await stripe.charges.create({
amount: this.amount * 100,
source: this.cardToken,
currency: this.currency
});
}
async refund() {
await this.validateRefund();
return await stripe.refunds.create({ charge: this.chargeId });
}
async validateCard() {
// Card validation logic
}
}
class RecurringCreditCardPayment {
constructor(creditCardPayment, schedule) {
this.creditCardPayment = creditCardPayment;
this.schedule = schedule;
}
async scheduleNextPayment() {
this.schedule.onNextCyle(() => {
await this.creditCardPayment.process();
})
}
}
const recurringCreditCardPayment = new RecurringCreditCardPayment(
new CreditCardPayment(),
new Schedule(),
);Por que isso importa: Pagamento Recorrente com Cartão de Crédito foca exclusivamente no agendamento e delega o processamento de pagamentos ao composto CreditCardPayment instância. Nenhuma herança significa nenhum acoplamento forte à implementação da classe pai. Alterações no processamento de cartão de crédito não afetam a lógica de agendamento. A instância de pagamento pode ser substituída por qualquer método de pagamento sem alterar o código de agendamento.
Conclusão
Utilize composição para separar responsabilidades em vez de misturá-las através de herança. Quando uma classe precisa da funcionalidade de outra classe, aceite-a como uma dependência e delegue a ela, em vez de herdar dela. Isso cria acoplamento fraco, facilita os testes e evita que alterações em uma classe quebrem outra.
.avif)
