Kural
Kalıtım yerine bileşimi tercih
edin. Derin kalıtım hiyerarşileri sıkı bağımlılık
oluşturur ve kodu anlamayı ve bakım yapmayı zorlaştırır.
Desteklenen diller: 45+Giriş
Kalıtım, üst ve alt sınıflar arasında sıkı bir bağımlılık oluşturarak kodu kırılgan ve değiştirmesi zor hale getirir. Bir sınıf davranışı miras aldığında, üst sınıfın uygulama detaylarına bağımlı hale gelir. Metotları geçersiz kılan (override) ancak yine de super çağıran alt sınıflar özellikle sorunludur; kendi mantıklarını miras alınan davranışla, üst sınıf değiştiğinde bozulacak şekillerde karıştırırlar. Bileşim (composition), nesnelerin diğer nesnelere delegasyon yapmasına izin vererek bu sorunu çözer; böylece gevşek bağ (loose coupling) ve net bir sorumluluk ayrımı sağlar.
Neden önemli?
Karışık sorumluluklar ve sıkı bağımlılık: Kalıtım, ilgili olmayan sorumlulukları aynı sınıf hiyerarşisinin içine zorla sokar. Bir ödeme işlemcisinden miras alan yinelenen ödeme sınıfı, zamanlama mantığını ödeme işlemleriyle karıştırır. Şunu çağırıp super.process() ardından kendi davranışınızı eklemeniz gerektiğinde, üst sınıfın uygulamasına sıkı sıkıya bağımlı olursunuz. Üst sınıfın process() metodu değiştiğinde, alt sınıf beklenmedik şekillerde bozulur.
İstenmeyen davranışların kalıtılması: Alt sınıflar, üst sınıflarından ihtiyaç duymadıkları veya farklı bir implementasyon gerektiren metodlar dahil her şeyi miras alır. Yinelenen (recurring) bir ödeme, refund() tek seferlik ödemeler için tasarlanmış mantığı miras alır, ancak abonelik iadelerinin işleyişi farklıdır. Ya metodları ezersiniz (override) ve kargaşaya yol açarsınız ya da uygunsuz kalıtım davranışıyla yaşamak zorunda kalırsınız.
Kırılgan üst sınıf (fragile base class) problemi: Üst sınıflarda yapılan değişiklikler tüm alt sınıfları etkiler. Ödemelerin nasıl işlendiğini CreditCardPayment değiştirmek, RecurringCreditCardPayment yapılan değişiklik zamanlama ile ilgisiz olsa bile sınıfını etkiler. Bu durum, hangi alt sınıfların bozulacağını öngöremeyeceğiniz için yeniden yapılandırmayı (refactoring) tehlikeli hale getirir.
Test etme karmaşıklığı: Kalıtım hiyerarşisinin derinliklerindeki sınıfları test etmek, üst sınıfın davranışını anlamayı gerektirir. Yinelenen ödeme zamanlamasını test etmek için kredi kartı işleme mantığı, Stripe API çağrıları ve doğrulama ile de uğraşmanız gerekir. Kompozisyon ise basit bir sahte (mock) ödeme nesnesiyle zamanlamayı test etmenize olanak tanır.
Kod örnekleri
❌ Kural dışı:
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
}Neden yanlış: RecurringCreditCardPayment ödeme işleme mantığını miras alır ancak asıl odak noktası ödemeler değil zamanlamadır. Metodu çağırıp super.process() zamanlama davranışı ile sarmalaması gerekir, bu da sıkı bir bağımlılık (tight coupling) yaratır. Sınıf, refund() üst sınıftan özellikler miras alır ancak abonelik iadeleri, tek seferlik ödemelerden farklı bir mantık gerektirir. Üzerindeki değişiklikler, CreditCardPayment bu değişiklikler zamanlama ile alakasız olsa bile RecurringCreditCardPayment sınıfını etkiler.
✅ Kurallara uygun:
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(),
);Neden önemli: RecurringCreditCardPayment yalnızca zamanlamaya odaklanır ve ödeme işlemeyi oluşturulan (composed) CreditCardPayment örneğine devreder. Kalıtım olmaması, üst sınıf implementasyonuna sıkı bağımlılık olmadığı anlamına gelir. Kredi kartı işleme süreçlerindeki değişiklikler zamanlama mantığını etkilemez. Ödeme örneği, zamanlama kodunu değiştirmeden herhangi bir ödeme yöntemiyle değiştirilebilir.
Sonuç
Kalıtım yoluyla karıştırmak yerine sorumlulukları ayırmak için kompozisyon kullanın. Bir sınıf başka bir sınıfın işlevselliğine ihtiyaç duyduğunda, bunu miras almak yerine bir bağımlılık olarak kabul edin ve işi ona devredin (delegate). Bu, gevşek bağımlılık (loose coupling) yaratır, test etmeyi kolaylaştırır ve bir sınıftaki değişikliklerin diğerini bozmasını engeller.

